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:
-
Headers — X-Amzn-Bedrock-GuardrailIdentifier,
X-Amzn-Bedrock-GuardrailVersion, optional
X-Amzn-Bedrock-Trace. The SDK forwards these correctly.
-
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:
- 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.
- `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.
Summary
When using
claude-agent-sdkwith Amazon Bedrock and a Bedrock Guardrailattached via the
X-Amzn-Bedrock-GuardrailIdentifier/X-Amzn-Bedrock-GuardrailVersionheaders (e.g. injected throughANTHROPIC_CUSTOM_HEADERS), everyInvokeModel/InvokeModelWithResponseStreamcall fails closed with:The model is never invoked, the guardrail never evaluates, no usage is
billed beyond the round-trip, and no
guardrailTraceevent 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-7on 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:
Headers —
X-Amzn-Bedrock-GuardrailIdentifier,X-Amzn-Bedrock-GuardrailVersion, optionalX-Amzn-Bedrock-Trace. The SDK forwards these correctly.Body — when the headers are present, Bedrock requires:
amazon-bedrock-guardrailConfigobject(e.g.
{\"streamProcessingMode\": \"sync\"}), andmessages[]and the systemprompt 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
guardContentandguardrailConfigsections).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:
`guardContent` markers; leave `tool_use` / `tool_result` blocks
untouched.
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:
fragile, will silently double-wrap once the SDK adds real support
unless a feature flag is plumbed.
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
does not. Migrating the SDK's Bedrock transport to Converse would
also resolve this (and prompt-caching [FEATURE] Prompt Caching Support in Claude Agent SDK #626) but is a larger change.
Environment
applies to any caller that adds guardrail headers.