Skip to content

fix: json ShapeDeserializer panic on union nested directly inside a union#672

Closed
ryunosuke121 wants to merge 1 commit into
aws:mainfrom
ryunosuke121:fix-nested-union-deserialization
Closed

fix: json ShapeDeserializer panic on union nested directly inside a union#672
ryunosuke121 wants to merge 1 commit into
aws:mainfrom
ryunosuke121:fix-nested-union-deserialization

Conversation

@ryunosuke121

Copy link
Copy Markdown

Fixes #671

Problem

json.(*ShapeDeserializer).ReadUnion panics with slice bounds out of range [1:0] when a union member's value is itself a union (e.g. bedrockagentcorecontrol GetGatewayTarget's TargetConfigurationmcpMcpTargetConfiguration). This currently crashes terraform-provider-aws v6.48.0 on every refresh of aws_bedrockagentcore_gateway_target.

ReadUnion detected "resume after reading a member value" by checking whether the top of the context stack is ctxUnion. For a union nested directly inside a union, the inner call sees the parent's ctxUnion, wrongly resumes, skips the inner '{', and passes that 1-byte token to memberFromToken → panic.

Fix

  • ReadUnion: when the top of the stack is ctxUnion, disambiguate by peeking. At a value position the next token can only be '{' (or null); on resume it can only be a member name ('"') or '}'. The parser's grammar state machine guarantees these sets are disjoint, so the peek is a sound discriminator. Behavior is unchanged for all non-nested cases.
  • memberFromToken: validate the token shape and return an error instead of panicking (defense in depth).
  • smithy.ReadUnion helper (serde.go): when the first d.ReadUnion returns no member (union value was null, {}, or contained only null members), the union has already been fully consumed — calling d.ReadUnion again read tokens past the union and corrupted the stream. Return early instead.

Testing

  • New regression tests in shape_deserializer_union_test.go:
    • nested union value (panicked before this change, with the exact production stack trace)
    • union member with null value (read past the union before this change)
    • flat union (behavior unchanged)
  • go test ./... passes across the repository (includes the JSONTestSuite corpus and fuzz seed corpora).

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Co-Authored-By: Claude Opus 4.8 (1M context)

…nion

ReadUnion decided whether it was resuming an in-progress union read by
checking whether the top of the context stack was ctxUnion. When a union
member's value is itself a union, the inner ReadUnion call saw the
parent's ctxUnion, wrongly resumed, skipped the inner '{' and passed the
1-byte token to memberFromToken, panicking with
"slice bounds out of range [1:0]".

Disambiguate by peeking: at a value position the next token can only be
'{' (or null), while on resume it can only be a member name or '}' --
disjoint sets given the parser's grammar state machine.

Also:
- memberFromToken: validate the token shape instead of panicking.
- smithy.ReadUnion helper: stop reading when the first ReadUnion call
  returns no member (union value was null/empty/all-null members);
  reading again consumed tokens past the union.

Fixes aws#671

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lucix-aws

Copy link
Copy Markdown
Contributor

thanks for the patch but closing in favor of #673 which solves this a little more directly

@lucix-aws lucix-aws closed this Jun 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

json ShapeDeserializer panics (slice bounds out of range) when a union member's value is itself a union

2 participants