anthropics / anthropics/claude-agent-sdk-python

subprocess CLI rejects list-form system_prompt (Anthropic API supports it)

Aberta
#899 3 comentários 0 reações 0 responsáveis Ver no GitHub
bug enhancement good first issue
Linguagem predominante
Python
Estrelas
8.1k
Forks
1.3k
Merge médio
2d 31min
PRs com merge (30d)
1

Descrição

## Summary

`SubprocessCLITransport._build_command` at `src/claude_agent_sdk/_internal/transport/subprocess_cli.py:209-220` only accepts `system_prompt` as one of:

- `str`
- `None`
- `{"type": "file", "path": ...}` (`SystemPromptFile`)
- `{"type": "preset", "append": ...}` (`SystemPromptPreset`)

When a caller passes the **list-of-content-blocks** form (which is valid at the upstream Anthropic Messages API per the `system` parameter docs), the transport raises:

```
AttributeError: 'list' object has no attribute 'get'
File ".../subprocess_cli.py", line 215, in _build_command
if sp.get("type") == "file":
```

before any model call. The crash happens at command-build time; the user-facing `ClaudeAgentOptions(system_prompt=...)` doesn't validate the shape, so the failure is delayed and noisy.

## Use case

Anthropic's `prompt_caching` documentation specifically recommends passing `system` as a **list of content blocks** so `cache_control` can be attached to a trailing block and yield layer-independent caching of role vs. injected reference material. We use this pattern to layer (1) a stable role system prompt with (2) per-call retrieval-style skill content. The cache_proxy in our stack handles the list form correctly; only the SDK's bundled CLI transport blocks it.

Worked example we hit today (paid validation, $0.46 burned before crash):

```python
options = ClaudeAgentOptions(
system_prompt=[
{"type": "text", "text": role_system_prompt},
{"type": "text", "text": injected_skill_content},
],
...
)
async for msg in query(prompt=user_prompt, options=options):
...
# AttributeError at SubprocessCLITransport._build_command
```

## Suggested fix

`_build_command` could:

1. Detect `isinstance(sp, list)` and serialize via a new flag, e.g. `--system-prompt-json ` pointing at a temp file containing the JSON-encoded list. The bundled `claude` CLI would parse that and forward as-is to the API.

2. Alternatively, accept the list form on the existing `--system-prompt-file` path with content auto-detection (string vs. JSON array).

Either keeps the wire format Anthropic-compatible and avoids the silent client-side flatten that today's only workaround (concatenate to string) forces.

## Workaround we're using

We collapse the list to a single concatenated string at the runtime layer and document the cost (loss of layer-independent caching) in our ADR. This regresses option 4 ("structured 2-block") to option 3 ("concatenated string") in our chosen design. We'd revert the regression as soon as the SDK forwards list-form.

## Reproduction

`claude-agent-sdk` 0.1.66, bundled CLI 2.1.119:

```python
from claude_agent_sdk import ClaudeAgentOptions, query
options = ClaudeAgentOptions(
system_prompt=[{"type": "text", "text": "hello"}, {"type": "text", "text": "world"}],
model="claude-opus-4-7",
)
async for msg in query(prompt="hi", options=options):
print(msg)
# AttributeError: 'list' object has no attribute 'get'
```

## Related

- Upstream Anthropic API reference: `system` parameter accepts `string | content_block[]` per [Messages API docs](https://docs.claude.com/en/api/messages).
- Caching guidance: [prompt caching](https://docs.claude.com/en/build-with-claude/prompt-caching) recommends placing `cache_control` on the LAST block of each cacheable layer.

Guia de contribuição

Nenhum guia de contribuição indexado para este repositório

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.