AWS Bedrock un-escapes tool-call arguments in transit
medium · inference · probed live 2026-07-26 · re-run 2026-08-25 · affects AWS Bedrock (Chat Completions), AWS Bedrock (Responses)
At temperature 0, every other provider agrees - across independent serving stacks - on the exact
bytes the model returns for a tricky string argument: escape sequences as literal
two-character text (\ + n), matching the JSON text the
model was shown. Both Bedrock lanes alone return different bytes: real control characters, one
un-escape pass beyond the consensus. (Through July both lanes also emitted a stray
content fragment beside the call; that stopped by 2026-08-25, and this finding
now covers the un-escape alone.)
What was sent
A record tool and the instruction to pass the payload
byte-for-byte (download):
{
"model": "google.gemma-4-31b",
"messages": [
{
"role": "system",
"content": "You are a data-entry agent. Call the `record` tool exactly once, passing EXACTLY the arguments given — byte for byte, no additions, no reformatting. Do not repeat the arguments in your reply."
},
{
"role": "user",
"content": "Record this payload:\n{\n \"note\": \"line1\\nline2\\t\\\"quoted\\\" and \\\\backslash\",\n \"unicode\": \"héllo — 世界 🚀\",\n \"nested\": {\n \"a\": [\n 1,\n 2.5,\n false,\n null\n ],\n \"b\": {\n \"c\": \"deep\"\n }\n },\n \"long\": \"LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL-END\"\n}"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "record",
"description": "Records a data payload verbatim.",
"parameters": {
"type": "object",
"properties": {
"note": {
"type": "string"
},
"unicode": {
"type": "string"
},
"nested": {
"type": "object"
},
"long": {
"type": "string"
}
},
"required": [
"note",
"unicode",
"nested",
"long"
]
}
}
}
],
"temperature": 0,
"max_tokens": 800
}
What came back
The other providers' note, decoded from the returned arguments JSON - 38
bytes, escapes preserved as text:
line1\nline2\t"quoted" and \\backslash
Both Bedrock lanes, byte-identical to each other - 35 bytes, real control characters: a real
line feed (0x0A) after line1, a real tab (0x09) after line2, and a
single backslash (0x5C):
line1␊line2␉"quoted" and \backslash
How this breaks expectations
An argument's bytes are the application's data. Two lanes disagreeing with the nine-provider consensus means the same model call yields different bytes depending on provider - any argument whose bytes matter (embedded JSON, code, regexes) changes meaning in transit.
Why it matters
JSON.parse(arguments) gives your tool different data on Bedrock than everywhere
else.
Run it yourself
curl "$BASE_URL/chat/completions" -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d @record-payload.chat.json
POST the body to both a Bedrock lane and any other provider; diff the decoded
note bytes.