anthropics / anthropics/anthropic-sdk-python

transform_schema raises AssertionError on valid list-form types, e.g. {"type": ["string", "null"]}

Aperta
#1,764 2 commenti 2 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
Python
Stelle
3.9k
Fork
853
Merge medio
1g 18h
PR unite (30g)
11

Descrizione

### 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

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.