Skip to content

[FEATURE] Native Bedrock Guardrail body wrapping for InvokeModel / InvokeModelWithResponseStream #999

@psaghelyi

Description

@psaghelyi

Summary

When using claude-agent-sdk with Amazon Bedrock and a Bedrock Guardrail
attached via the X-Amzn-Bedrock-GuardrailIdentifier /
X-Amzn-Bedrock-GuardrailVersion headers (e.g. injected through
ANTHROPIC_CUSTOM_HEADERS), every InvokeModel /
InvokeModelWithResponseStream call fails closed with:

HTTP 400  Guardrail was enabled but input is in incorrect format.

The model is never invoked, the guardrail never evaluates, no usage is
billed beyond the round-trip, and no guardrailTrace event is produced —
so guardrails can be neither enforced nor observed while routing
through this SDK against Bedrock.

Reproduced on claude-agent-sdk==0.2.87 + us.anthropic.claude-opus-4-7
on Bedrock us-east-1, both single-agent and multi-agent (sub-agent /
A2A) configurations.

Why it fails

Bedrock Guardrail enforcement on the InvokeModel path requires two
coordinated changes
per request:

  1. HeadersX-Amzn-Bedrock-GuardrailIdentifier,
    X-Amzn-Bedrock-GuardrailVersion, optional
    X-Amzn-Bedrock-Trace. The SDK forwards these correctly.

  2. Body — when the headers are present, Bedrock requires:

    • a top-level amazon-bedrock-guardrailConfig object
      (e.g. {\"streamProcessingMode\": \"sync\"}), and
    • natural-language text segments in messages[] and the system
      prompt wrapped in
      <amazon-bedrock-guardrails-guardContent_<tag>>...</...>
      markers so the engine knows which spans to evaluate.
      Tool-use / tool-result blocks must NOT be wrapped.

    The SDK currently emits Anthropic Messages-shaped bodies on the
    Bedrock path with neither of these, so Bedrock fails closed.

Reference: AWS Bedrock Guardrails — Use a guardrail with the
InvokeModel API
(the guardContent and guardrailConfig sections).

Requested behavior

When `ClaudeAgentOptions` is configured for Bedrock and the runtime
detects guardrail headers (or via an explicit option, e.g.
`bedrock_guardrail={"id": "...", "version": "DRAFT"}`), the SDK should:

  • Inject `amazon-bedrock-guardrailConfig` into the outgoing body.
  • Wrap text content blocks in `messages[]` and the system prompt with
    `guardContent` markers; leave `tool_use` / `tool_result` blocks
    untouched.
  • Pass through `guardrailTrace` / intervention metadata on the
    response side so callers can attribute guardrail actions to a turn.

Equivalent support is already needed on
`InvokeModelWithResponseStream` for the streaming code path.

Why this matters

Without native support, integrators have two choices, both bad:

  1. Local botocore hook that monkey-patches the outgoing body —
    fragile, will silently double-wrap once the SDK adds real support
    unless a feature flag is plumbed.
  2. `ApplyGuardrail` sidecar calls — possible for observability
    but does not enforce; adds 1–2 extra round-trips per turn.

Native support lets every `claude-agent-sdk` user pick up Bedrock
Guardrail enforcement and the corresponding `guardrailTrace`
observability with a single config option, the same way Bedrock
Guardrail headers already work today.

Related

Environment

  • `claude-agent-sdk==0.2.87`
  • AWS Bedrock `us-east-1`
  • `us.anthropic.claude-opus-4-7`
  • Reproduced on AWS Bedrock AgentCore Runtime; same body-shape issue
    applies to any caller that adds guardrail headers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions