Bedrock: resumed sessions silently drop prior turns' thinking blocks — model-name comparison never matches a prefixed model id

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 1 comment · opened Jul 29, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Resuming a session on Bedrock, the request is built with all prior assistant turns' thinking blocks removed. They are present in the transcript; they do not reach the model. There is no error and no warning, so an agent silently loses the reasoning behind its own earlier replies on every resumed turn.

The model name recorded against a past reply and the model being called are written differently for the same model on Bedrock:

recorded in the transcript against each past reply     claude-sonnet-4-6
the model configured and called                        eu.anthropic.claude-sonnet-4-6

These can never be equal. claude-sonnet-4-6 is not a valid Bedrock identifier at all — get-foundation-model returns ValidationException. The callable forms are anthropic.claude-sonnet-4-6 or the inference profile eu.anthropic.claude-sonnet-4-6, and both carry a prefix. Rewriting the recorded name to match the configured id, changing nothing else, restores all the thinking blocks to the request.

What Should Happen?

Resuming a session on Bedrock should send prior turns' thinking blocks, as resuming the same session against the Anthropic API does. Two identifiers naming the same model should not be treated as a model change.

If they are stripped deliberately, it should be visible — an equivalent request against the Anthropic API carries the blocks, so today the two transports differ silently.

Error Messages/Logs

Steps to Reproduce

  1. Save this capture endpoint, which prints the model, message count and thinking-block count of each request and then rejects it:

```python
# capture.py
import json
from http.server import BaseHTTPRequestHandler, HTTPServer

class H(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers.get("content-length") or 0))
try:
msgs = json.loads(body).get("messages", [])
except Exception:
msgs = []
n = sum(1 for m in msgs if isinstance(m.get("content"), list)
for b in m["content"] if isinstance(b, dict) and b.get("type") == "thinking")
if len(msgs) > 1:
print(f"POST {self.path}\n messages={len(msgs)} thinking_blocks={n}", flush=True)
self.send_response(400); self.end_headers()
def log_message(self, *a): pass

HTTPServer(("127.0.0.1", 8787), H).serve_forever()
```

Requests with a single message are startup probes and are filtered out.

  1. Run a Bedrock session of two or three turns so its transcript accumulates thinking blocks, then confirm they are on disk and note the recorded model name:

``bash
F=~/.claude/projects/<project>/<session-id>.jsonl
grep -c '"type":"thinking"' "$F" # e.g. 5
grep -o '"model":"[^"]*"' "$F" | sort -u # "model":"claude-sonnet-4-6"
``

  1. Start python3 capture.py.
  1. Resume that session with the request redirected to the capture endpoint:

``bash
CLAUDE_CODE_USE_BEDROCK=1 \
ANTHROPIC_BEDROCK_BASE_URL=http://127.0.0.1:8787 \
ANTHROPIC_MODEL=eu.anthropic.claude-sonnet-4-6 \
AWS_REGION=eu-west-1 \
claude --resume <session-id> -p "continue" < /dev/null
``

The captured request contains the full conversation and thinking_blocks=0, against the count from step 2.

  1. Repeat against the Anthropic API for contrast — unset CLAUDE_CODE_USE_BEDROCK and ANTHROPIC_MODEL, set ANTHROPIC_BASE_URL=http://127.0.0.1:8787 and an API key. The same conversation arrives with every thinking block present.
  1. Optionally isolate the naming: repeat step 4 with ANTHROPIC_MODEL set to each of anthropic.claude-sonnet-4-6 and anthropic.claude-sonnet-4-5-20250929-v1:0. All Bedrock forms give thinking_blocks=0, so it is not specific to the cross-region inference profile or to a model version.

Observed on one transcript, varying only the transport and model id:

| Transport and model | Thinking blocks in request |
|---|---|
| Anthropic API, claude-sonnet-4-6 | 5 of 5 |
| Bedrock, eu.anthropic.claude-sonnet-4-6 | 0 of 5 |
| Bedrock, anthropic.claude-sonnet-4-6 | 0 of 5 |
| Bedrock, anthropic.claude-sonnet-4-5-20250929-v1:0 | 0 of 5 |

Note on method: these captures drove the resume through the Agent SDK's session_store rather than a local claude --resume, since the transcript was held in S3. The steps above are the shorter local equivalent.

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.220 (Claude Code)

Platform

AWS Bedrock

Operating System

macOS

Terminal/Shell

Non-interactive/CI environment

Additional Information

_No response_

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗