# Pydantic AI + Anthropic: "Container identifier can only be provided when using the code execution tool" (400)

*Disclosure: I work on MemorySync (I'm Mohammed, the founder). I drafted this page with AI assistance and checked it against the source issue, the merged fix PR, the current pydantic-ai 2.66.0 source and the Pydantic AI Anthropic docs listed under Sources on Nov 6, 2026. The original two workarounds ran offline on 2.63.0 on Oct 29. I re-ran that request-body check offline on 2.66.0 on Nov 6 with a mocked HTTP client (no Anthropic API calls); the ordinary follow-up sent no container, and the other two paths sent none. The snippets below are adapted from that check; I didn't run them against the live API.*

## The question

In [pydantic/pydantic-ai#11761](https://github.com/pydantic/pydantic-ai/issues/11761) (opened Oct 28, 2026), a run that used Anthropic code execution works, but the next run on the same message history, without code execution, fails:

```text
400: Container identifier can only be provided when using the code execution tool
```

The repro from the issue, trimmed:

```python
from pydantic_ai import Agent
from pydantic_ai.capabilities import NativeTool
from pydantic_ai.native_tools import CodeExecutionTool

model = 'anthropic:claude-sonnet-5-5'
first = Agent(model, capabilities=[NativeTool(CodeExecutionTool())]).run_sync(
    'Use the code execution tool to compute 3 * 12390.'
)
# 400: Container identifier can only be provided when using the code execution tool
Agent(model).run_sync('Say the result in one sentence.', message_history=first.all_messages())
```

The report is on `main` just after v2.63.0. The fix shipped in 2.65.0; 2.66.0 is the latest release on PyPI as of Nov 6, 2026. This affects an app on 2.64.0 or earlier that saves `all_messages()` and replays it later: the stored history keeps the container, and the next request may not have code execution.

**Status (checked Nov 6, 2026):** resolved. #11761 closed on Nov 3 at 00:14 IST, 1 comment, when #11764 merged on Nov 3 at 00:14 IST. The fix ships in 2.65.0; the latest release is 2.66.0. In my current 2.66.0 offline request check, the default toolless follow-up sent no `container`.

> **Skip the memory plumbing around this fix.** Once you've upgraded to 2.65.0 or later, the [MemorySync Pydantic AI guide](https://docs.memorysync.io/guides/pydantic-ai?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_pydantic-ai-anthropic-container-identifier-code-execution-400) shows how to add per-user memory with MemorySync. MemorySync doesn't fix this error itself. When you're ready, [see MemorySync](https://memorysync.io/?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_pydantic-ai-anthropic-container-identifier-code-execution-400). Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings â†’ Billing).

## Why it happens

When Anthropic returns a container, `AnthropicModel` saves its ID on the response as `provider_details['container_id']`. Before 2.65.0, `_get_container()` scans the history backwards on every later request and sends the most recent `container_id` it finds, whatever tools the new request has. From 2.65.0, the ordinary request without code execution returns before that history scan. The [Pydantic AI Anthropic docs](https://pydantic.dev/docs/ai/models/anthropic/) describe this: "When you continue a run from message history, Pydantic AI automatically reuses the Anthropic code execution container recorded in that history on requests that have code execution: CodeExecutionTool, or WebSearchTool or WebFetchTool where code execution may call themâ€¦ The Anthropic API rejects a container on any other request, such as one with no tools, so Pydantic AI doesn't send it there."

Per the issue, the Anthropic API only accepts a container on a request with code execution: `CodeExecutionTool`, or the `20260209` web tools, which Anthropic calls from code execution by default. A plain follow-up gets the 400.

## Fallback 1 (for 2.64.0 or earlier): set `anthropic_container=False` on the follow-up

`AnthropicModelSettings.anthropic_container` already has an off switch. Its docstring says: "Set to `False` to force a fresh container (ignore any `container_id` from history)." In 2.63.0, `False` makes `_get_container()` return no container, so the request goes out without one:

```python
from pydantic_ai import Agent
from pydantic_ai.models.anthropic import AnthropicModelSettings

follow_up = Agent('anthropic:claude-sonnet-5-5')  # no code execution here
result = follow_up.run_sync(
    'Say the result in one sentence.',
    message_history=first.all_messages(),
    model_settings=AnthropicModelSettings(anthropic_container=False),
)
```

This only changes that request. The history still holds the old `container_id`, so a later run that does use code execution (without the setting) can pick it up again.

In my Oct 29 offline check on 2.63.0, the default follow-up sent `container_abc` from history, and the same call with `anthropic_container=False` sent no `container` field. On 2.66.0 the default toolless follow-up already sends no container.

## Fallback 2 (for 2.64.0 or earlier): strip `container_id` from stored history before replaying it

If you load history from your own store and don't know in advance which agent will run next, remove the ID when you load it:

```python
from dataclasses import replace
from pydantic_ai.messages import ModelResponse

def strip_container_ids(messages):
    out = []
    for m in messages:
        if isinstance(m, ModelResponse) and m.provider_details and 'container_id' in m.provider_details:
            details = {k: v for k, v in m.provider_details.items() if k != 'container_id'}
            m = replace(m, provider_details=details or None)
        out.append(m)
    return out

result = follow_up.run_sync('Say the result in one sentence.',
                            message_history=strip_container_ids(history))
```

`replace()` returns new objects, so the original list isn't changed (my check confirmed that). The cost: a later code execution run on this history won't reconnect to the old container, so files or state from it are gone. Only strip when the next request has no code execution, or when you don't need the container.

## Upgrade or fallback

If the follow-up agent also has `CodeExecutionTool`, the container is accepted. Upgrade to 2.65.0 or later for the merged #11764 fix: `_get_container()` returns no container when the request has no code execution tool and its other tool setup provides none. There is no new `AnthropicModelProfile` switch; that proposed flag was removed before merge. If you pass `extra_body['tools']`, the released code does not inspect that list and still sends the history container. For that case, remove the ID from history or explicitly set `anthropic_container=False` as appropriate for your request.

## Checklist

1. Did an earlier run in this history use Anthropic code execution? Look for `provider_details['container_id']` on a `ModelResponse`.
2. Does the next request lack `CodeExecutionTool` and the `20260209` web tools? Then you're on the path that fails.
3. On 2.64.0 or earlier, pass `AnthropicModelSettings(anthropic_container=False)` on that request, or strip `container_id` when you load history.
4. On 2.65.0 or later, check the default request body once with a mock. The upstream fix removes the need for these history workarounds in the ordinary case.

## Common questions

**Does this happen with no tools at all?**
On 2.64.0 or earlier, yes. On 2.65.0 or later, the ordinary toolless follow-up no longer sends it, which my offline check confirms. The issue's follow-up agent has no tools, and per the PR the API rejects a container "on any other request, such as one with no tools".

**Is this the same as the expired-container error?**
No. The docs say containers expire after 30 days and a request that names an expired one returns an error. This 400 is about sending a valid container on a request without code execution.

**Does the PR change Bedrock or Vertex?**
Per the merged 2.65.0/2.66.0 source, client profiles that do not accept file and container IDs still return no container. The shared guard then checks whether the request provisions code execution. I didn't run Bedrock or Vertex for this page.

## Skip the plumbing with MemorySync

Disclosure: I build MemorySync.

It doesn't fix this bug. Upgrade first. On 2.64.0 or earlier you still need Fallback 1 or Fallback 2. The [MemorySync Pydantic AI guide](https://docs.memorysync.io/guides/pydantic-ai?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_pydantic-ai-anthropic-container-identifier-code-execution-400) documents `MemorySyncCapability`, which adds recall through Pydantic AI's instructions channel (`ModelRequest.instructions`) rather than as a conversation part. After each successful run it sends the user's prompt to fact extraction and stores the durable facts; model replies, tool calls and thinking parts aren't sent. So a user's facts can come back in later runs even if you trim or rebuild the stored history, but code execution output and containers are not part of memory. Read the [guide](https://docs.memorysync.io/guides/pydantic-ai?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_pydantic-ai-anthropic-container-identifier-code-execution-400) first. For an overview of MemorySync, see [the MemorySync homepage](https://memorysync.io/?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_pydantic-ai-anthropic-container-identifier-code-execution-400).

From the guide:

**Unverified (not run):**

```bash
pip install pydantic-ai-memorysync
```

```python
from pydantic_ai import Agent
from pydantic_ai_memorysync import MemorySyncCapability

agent = Agent(
    "openai:your-model",
    instructions="You are a helpful assistant.",
    capabilities=[MemorySyncCapability(user_id="customer-7")],
)

result = await agent.run("What should I cook tonight?")
# The model saw:
#   You are a helpful assistant.
#
#   Relevant memories about this user from previous conversations:
#   - Is vegetarian
#   - Flies aisle
#
# ...and the durable facts in this prompt are remembered for
# every future run.
```

*Snippet adapted from the [MemorySync Pydantic AI guide](https://docs.memorysync.io/guides/pydantic-ai?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_pydantic-ai-anthropic-container-identifier-code-execution-400); I haven't run it for this page.*

**Try it:** MemorySync is optional here: the steps above work without it. If you want the memory layer from the snippet, [see MemorySync](https://memorysync.io/?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_pydantic-ai-anthropic-container-identifier-code-execution-400), create an API key in the dashboard and drop the snippet into your agent.

## Sources

- github.com/pydantic/pydantic-ai/issues/11761 (opened 2026-10-28; closed Nov 3, 2026 00:14 IST, 1 comment; read 2026-11-06)
- github.com/pydantic/pydantic-ai/pull/11764 (merged Nov 3, 2026 00:14 IST; description and diff read 2026-11-06); fix included in v2.65.0 release notes
- pydantic-ai source, v2.66.0: pydantic_ai_slim/pydantic_ai/models/anthropic/__init__.py (`_get_container` now checks `provisions_code_execution(tools)` and returns no container in the ordinary no-code-execution case; read 2026-11-06)
- pydantic.dev/docs/ai/models/anthropic/ ("Code Execution Containers"; ai.pydantic.dev/models/anthropic/ redirects here; read 2026-11-06)
- Offline wire check against pydantic-ai-slim[anthropic] 2.63.0 with a mocked HTTP transport (2026-10-29); same check re-run on 2.66.0 (Nov 6, 2026): default toolless follow-up `<absent>`, `anthropic_container=False` `<absent>`, stripped history `<absent>`; original history retained `container_abc`
- MemorySync docs: docs.memorysync.io/guides/pydantic-ai

**Next step:** read the [MemorySync Pydantic AI guide](https://docs.memorysync.io/guides/pydantic-ai?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_pydantic-ai-anthropic-container-identifier-code-execution-400), then [see MemorySync](https://memorysync.io/?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_pydantic-ai-anthropic-container-identifier-code-execution-400). Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings â†’ Billing).
