anthropics / anthropics/anthropic-sdk-python
transform_schema raises AssertionError on valid list-form types, e.g. {"type": ["string", "null"]}
- 主要言語
- Python
- スター
- 3.9k
- フォーク
- 853
- 平均マージ
- 1日 18時間
- マージ済み PR(30日)
- 11
説明
### Summary
`transform_schema` (the structured-output schema transformer used by `messages.parse` / `beta.messages.parse`) crashes with an `AssertionError` on any schema that uses the list form of `type` — e.g. `{"type": ["string", "null"]}` — which is valid JSON Schema and explicitly supported by the Anthropic structured-outputs API.
The failure happens at request-build time, before any API call.
### Repro
```python
from anthropic import transform_schema
# Valid JSON Schema; also documented as supported by the structured-outputs API
transform_schema({"type": ["string", "null"]})
```
One-liner:
```bash
python -c 'from anthropic import transform_schema; transform_schema({"type": ["string", "null"]})'
```
Actual (on `anthropic==0.112.0`; the relevant code is unchanged on current `main` of `src/anthropic/lib/_parse/_transform.py`):
```
AssertionError: Expected code to be unreachable, but got: ['string', 'null']
```
A more realistic nested variant fails the same way:
```python
transform_schema({
"type": "object",
"properties": {"name": {"type": ["string", "null"]}},
"required": ["name"],
"additionalProperties": False,
})
```
### Expected
An equivalent transformed schema, e.g. rewriting the type union into `anyOf` branches:
```json
{"anyOf": [{"type": "string"}, {"type": "null"}]}
```
### Why this should be supported
- JSON Schema allows `type` to be "a string or an array of strings" — the list form is standard, not an extension.
- The [structured outputs docs](https://docs.claude.com/en/docs/build-with-claude/structured-outputs) explicitly document type arrays as supported (and meter them): *"Total parameters that use `anyOf` or type arrays (for example, `"type": ["string", "null"]`) across all strict schemas."* So the API accepts this shape; only the SDK-side transformer rejects it.
- Nullable-type unions are pervasive in real schemas: Pydantic `Optional[...]` fields in older/alternative generators, OpenAPI 3.1 output, GraphQL-derived schemas, and MCP tool `inputSchema`s commonly emit `"type": [..., "null"]`.
### Root cause
In `src/anthropic/lib/_parse/_transform.py`, `type_` is typed and handled as a scalar:
```python
type_: Optional[SupportedTypes] = json_schema.pop("type", None)
...
elif type_ == "boolean" or type_ == "integer" or type_ == "number" or type_ == "null" or type_ is None:
pass
else:
assert_never(type_)
```
A list value matches none of the scalar branches and falls into `assert_never`.
### Suggested fix
When `type_` is a list, canonicalize it before the scalar handling, e.g. rewrite `{"type": [t1, t2, ...], **rest}` into an `anyOf` of single-type branches (with sibling constraints attached to the non-`null` branches) and recurse. Single-element lists can collapse to the scalar form.
### Related (not duplicates)
- #1691 handles empty/typeless `{}` sub-schemas (`ValueError` path) — a different gap in the same function.
- #1185 discusses grammar-size limits and mentions type arrays as API-supported; it does not cover this SDK-side crash.
### Environment
- `anthropic==0.112.0` (also reproduces against `main`'s `_transform.py`)
- Python 3.12.13, macOS arm64
コントリビューションガイド
評価
この issue はまだ評価されていません。