anthropics / anthropics/anthropic-sdk-python

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

Abierto
#1,764 2 comentarios 2 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Python
Estrellas
3.9k
Forks
853
Merge medio
1 d 18 h
PR fusionados (30 d)
11

Descripción

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

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.