Skip to content

▚▚ fix(sdk/python): send the SigV4-signed payload bytes to Bedrock - #1163

Open
breken-ai wants to merge 1 commit into
superagent-ai:mainfrom
breken-ai:fix/bedrock-sigv4-payload-bytes
Open

breken-ai wants to merge 1 commit into
superagent-ai:mainfrom
breken-ai:fix/bedrock-sigv4-payload-bytes

Conversation

@breken-ai

@breken-ai breken-ai commented Sep 14, 2026 •

Copy link
Copy Markdown

Calling the Python SDK with a bedrock/... model fails on every request: AWS returns a SigV4 signature mismatch, because the payload hash the SDK signs is computed over different bytes than the ones httpx actually sends.

Root cause

call_provider in sdk/python/src/safety_agent/providers/__init__.py signs the Bedrock request over json.dumps(request_body) (default ", " / ": " separators) and puts that hash in x-amz-content-sha256 and the canonical request. It then posts the request with json=request_body. httpx >= 0.28 serialises json= with compact (",", ":") separators, so the body on the wire no longer matches the signed payload. pyproject.toml only requires httpx>=0.27.0 and no lockfile is committed, so a fresh uv sync installs 0.28.x and every Bedrock call is rejected.

Fix

  • sdk/python/src/safety_agent/providers/__init__.py: build a request_kwargs dict that is {"json": request_body} for every provider and {"content": payload.encode("utf-8")} for bedrock, where payload is the exact string that was signed. The three client.post(...) sites use **request_kwargs instead of json=request_body. Behaviour for all non-bedrock providers is unchanged.

Tests

  • sdk/python/tests/test_bedrock_provider.py (new): mocks httpx.AsyncClient, calls call_provider("bedrock/..."), rebuilds the request with httpx.Request(...) from the kwargs that were passed to post, and asserts sha256(request.content) equals the x-amz-content-sha256 header. Fails on main (hash mismatch), passes with the fix.
  • uv run pytest in sdk/python: 153 passed.

Not changed

  • The signing itself (BedrockProvider.get_signed_headers) is untouched; it was correct for the string it was given.
  • Not verified against a live Bedrock endpoint (no AWS credentials available; the Python CI job does not receive AWS_BEDROCK_API_KEY either, which is why this never surfaced in CI).

▚▚ Shipped by breken — self-healing software. This one's on us. breken.ai


Note

Low Risk
Targeted HTTP transport change for Bedrock only; non-Bedrock providers still use json= and signing logic is unchanged.

Overview
Fixes Bedrock SigV4 failures caused by a mismatch between the payload bytes signed for AWS and the body httpx actually sends when using json=request_body (compact JSON in httpx 0.28+ vs the string from json.dumps used for signing).

call_provider now builds shared request_kwargs: default {"json": request_body} for other providers, and for bedrock {"content": payload.encode("utf-8")} where payload is the exact string passed into signing. All three client.post paths use **request_kwargs instead of hard-coded json=.

Adds test_bedrock_provider.py with a mocked httpx call that asserts x-amz-content-sha256 matches sha256 of the wire body rebuilt from the post kwargs.

Reviewed by Cursor Bugbot for commit 27776c4. Configure here.

call_provider signed the Bedrock request over json.dumps(request_body)
(default ", " / ": " separators) but then posted the dict through
httpx's json= parameter. httpx >= 0.28 serialises json= with compact
separators, so the bytes on the wire differ from the bytes that were
hashed into x-amz-content-sha256 and the canonical request, and AWS
rejects every Bedrock call with a signature mismatch.

Post the exact signed string via content= for the bedrock provider so
the hash and the body always agree; every other provider still uses
json=.
@vercel

vercel Bot commented Sep 14, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Superagent Team on Vercel.

A member of the Team first needs to authorize it.

@open-cla

open-cla Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Contributor License Agreement

All contributors are covered by a CLA.

This branch has not been deployed

No deployments
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.

1 participant