JS Wei (Jack) Sun

httpx2 swap breaks Anthropic and OpenAI SDK tracers, SELF hides ELF in SQLite

Two developer drops swap the substrate under familiar tools: an SDK dep bump breaks tracers, and SELF hides ELF inside SQLite.

httpx2 swap breaks Anthropic and OpenAI SDK tracers, SELF hides ELF in SQLite

TL;DR

  • Anthropic’s v1.0 SDK moves to httpx2, following OpenAI’s v3.0 two weeks earlier.
  • httpx2.Response fails isinstance checks, silently breaking OpenTelemetry tracers and test mocks.
  • Pydantic AI ≤2.32 users hit runtime crashes the moment the Anthropic cutover landed.
  • Farid Zakaria’s SELF packs ELF binaries into SQLite, shrinking a 723-exec userland to 611.9 MiB.
  • ps, gdb, and readelf no longer recognize the SELF format.

Today’s two developer tech drops sit on the same seam: what happens when you swap the substrate that familiar tools were built to read. Anthropic’s v1.0 SDK cut over to httpx2 this morning, following OpenAI’s v3.0 two weeks earlier — a routine dep bump that turns out to break isinstance checks against the old httpx.Response, silently disabling OpenTelemetry tracers, breaking test mocks, and crashing every Pydantic AI ≤2.32 deployment the instant the release hit PyPI.

The second story is the same move done on purpose. Farid Zakaria’s SELF repackages ELF binaries as rows in a SQLite database — strip becomes DELETE + VACUUM, ldd becomes a join, and 723 executables of userland shrink by ~5% through natural string deduplication. The cost is the same cost the SDK cutover imposes: ps, gdb, and readelf no longer recognize what they’re looking at. One drop is a research provocation, the other is production breakage, and both make the same point about how much tooling depends on the format staying still.

Anthropic and OpenAI SDKs jump to httpx2, break tracing

Source: simon-willison · published 2026-08-24

TL;DR

  • Anthropic’s v1.0 SDK switches from httpx to httpx2, following OpenAI’s v3.0 two weeks earlier
  • httpx2.Response fails isinstance checks against httpx.Response, silently breaking test mocks and OpenTelemetry tracers
  • Pydantic AI ≤2.32 users hit runtime crashes the moment the Anthropic cutover landed
  • Sampling params like temperature and top_p now return HTTP 400 on Fable 5 unless moved to extra_body

The two-week domino

Simon Willison’s llm-anthropic 0.27 release reads like a footnote — a plugin bump to track Anthropic’s SDK v1.0, itself a bump to swap httpx for Pydantic’s httpx2 fork. It isn’t a footnote. OpenAI made the identical swap in openai v3.0 roughly two weeks earlier, and the coordinated move is quietly reshaping the Python AI stack.

httpx2 is billed as a continuation, not a rewrite, but it ships real breakage: renamed internals (httpcorehttpcore2), dropped Python 3.9 support, and a switch to the OS trust store instead of certifi 1. The load-bearing gotcha is subtler: httpx2.Response is deliberately type-incompatible with httpx.Response, so any cross-library isinstance check silently returns False 1. Anthropic’s SDK also tightened the Messages API surface — temperature, top_p, and top_k have been removed from messages.create() and pushed into extra_body, and Fable 5 returns HTTP 400 if you send non-default values through the old path 2.

What breaks quietly

The migration’s real cost is observability rot. Tools like pytest-httpx and OpenTelemetry’s HTTPXClientInstrumentor patch the httpx namespace by name — they don’t see httpx2 traffic at all. Tests stay green while hitting the real network; spans disappear from APM dashboards without an error. Anthropic’s docs prescribe calling httpx2.alias_httpx() at startup as a workaround, but it must run before any other httpx import or it raises RuntimeError, and even then it doesn’t satisfy every tracer 3.

Willison himself warned about this shape a year ago: Python can’t install two versions of the same package, so any project with a transitive httpx<1 pin and an httpx2 dependency is stuck picking a side 4. The Anthropic v1.0 cutover made that abstract worry concrete — Pydantic AI ≤2.32 crashed at runtime for users who upgraded the SDK without upgrading the framework on top 5.

flowchart LR
    A[User code] --> B[anthropic v1.0]
    A --> C[openai v3.0]
    A --> D[Pydantic AI ≤2.32]
    B --> E[httpx2]
    C --> E
    D -->|expects| F[httpx]
    E -. isinstance fails .-x G[pytest-httpx / OTel tracer]
    F -. can't coexist .-x E

Agentic upgrades compress the review window

Willison’s own llm CLI hit a compressed 0.32.1 → 0.33 sequence to repair a broken transitive httpx pin after OpenAI’s v3.0 dropped it. Security reviewers flagged that kind of rapid dependency swap as a supply-chain change-control trigger — precisely the window where typosquatted forks and malicious releases land 6.

That’s the uncomfortable subtext of the 0.27 release notes. Willison shipped the port by handing Claude Code a one-line prompt — read MIGRATION.md and get the tests passing — and merging the resulting PR. It’s a productivity win, and the diff is small. But the change class is exactly the one that most deserves human eyes: a mandatory upgrade to a forked HTTP client, on a maintenance window measured in days, propagating through every plugin that touches an Anthropic or OpenAI SDK.

The plugin bump is fine. The wave underneath it is going to keep breaking things for months.


SELF makes ELF patching a SQL UPDATE, stripping a DELETE

Source: simon-willison · published 2026-08-24

TL;DR

  • Farid Zakaria’s SELF packs an ELF binary into a SQLite database, tagged with a 4-byte magic at offset 68.
  • A full 723-exec userland shrinks from 644.4 to 611.9 MiB via natural string/symbol deduplication.
  • strip becomes DELETE + VACUUM, ldd becomes a join, and a proof-of-concept self-ld does dynamic linking in SQL.
  • No shared text pages between processes, a ~5ms SQLite init tax per exec, and ps/gdb/readelf don’t recognize the format.

The design argument, not the stunt

Simon Willison flagged Farid Zakaria’s SELF (“Structured Executable & Linkable Format”) as a neat Linux trick: set bytes 68–71 of a SQLite file to the ASCII SELF, register a binfmt_misc handler for that magic, and the kernel will happily exec your database. A small C interpreter, self-exec, pulls segments out of the b-tree, maps them into memory, and jumps to the entry point.

But the trick is downstream of the actual claim. SELF is the second act of Zakaria’s sqlelf project, which exposed ELF internals as SQLite virtual tables so you could analyze binaries with joins instead of piping readelf into grep. His framing is that ELF is “a database that refuses to admit it” 7. SELF inverts the relationship: instead of putting SQL over ELF, the SQL rows are the format. patchelf collapses to UPDATE. strip becomes DELETE ... VACUUM. ldd is a view joining a symbols table against segments 78.

That’s the argument worth engaging with — ELF’s opacity is a UX problem, and a well-known relational format can dissolve most of the bespoke tooling around it.

The numbers are less silly than they sound

The obvious objection — “you’re wrapping a binary in a database, it’ll be twice the size” — doesn’t hold. A stripped single SELF weighs 1.79 MB against the original 1.76 MB ELF, roughly a 1% overhead 9. And because SQLite naturally deduplicates repeated strings and symbols, packing an entire userland (723 executables plus 400 shared libraries) into one database comes out to 611.9 MiB versus 644.4 MiB for the equivalent ELF tree 9. The database bundle is smaller than the pile of files it replaces.

The repo also ships self-ld, a proof-of-concept dynamic linker that performs symbol resolution and relocation patching entirely through SQL queries 8. That matters because it demonstrates the model extends past loading into the full linking pipeline — the SQL-native workflow isn’t just for introspection.

The blockers Zakaria doesn’t hide

The strongest critique comes from Zakaria himself:

Two processes running the same SELF binary do not share text pages the way a normally-mmap’d ELF does, because the bytes are copied out of the b-tree rather than mapped. 10

That single sentence rules out system-wide adoption today. Every running instance of /bin/ls-as-SELF would pay full RSS instead of sharing read-only pages across processes — a non-starter for a real userland. There’s also a fixed ~5ms SQLite init cost on every exec 9.

The toolchain friction is its own tax. gdb, nm, and readelf don’t understand the format, and because execution routes through binfmt_misc, ps and top show self-exec as the running process instead of the actual program 11. And LWN has separately surfaced kernel-developer concern that custom loader paths via binfmt_misc are “ripe for malicious loader injection attacks” 12 — a general critique of the mechanism SELF leans on, not of SELF itself, but relevant if anyone tried to ship this.

None of which invalidates the point. The interesting artifact here isn’t the executable-as-database; it’s the argument that if you treat your binary format as a database from day one, half of binutils becomes SELECT.

Footnotes

  1. Pydantic httpx2 migration docshttps://pydantic.dev/docs/httpx2/get-started/migration/

    httpx2 defaults to using the operating system’s trust store via truststore instead of the certifi bundle… Support for Python 3.9 was dropped, requiring Python 3.10+… httpx2.Response is type-incompatible with the original httpx.Response, making isinstance checks fail across library boundaries.

    2
  2. ecorpit.com — Anthropic SDK v1 breaking changes writeuphttps://ecorpit.com/anthropic-python-sdk-v1-httpx2-sampling-params-removed-docs-conflict-2026/

    Anthropic has significantly ‘cleaned’ the SDK surface by removing sampling parameters—temperature, top_p, and top_k—from core methods like messages.create()… newer generations, such as Claude Fable 5, return a 400 error for non-default sampling values.

  3. Anthropic release notes / migration guide (claude.com)https://platform.claude.com/docs/en/release-notes/overview

    The SDK documentation prescribes calling httpx2.alias_httpx() at startup… however, this workaround may not satisfy all tracing tools, leading to cases where outbound calls ‘quietly stop’ appearing in observability dashboards without raising errors.

  4. Simon Willison (Aug 2025) — earlier warning on httpx splithttps://simonwillison.net/2025/Aug/20/httpx-version/

    Splitting the library into two distinct packages (httpx vs httpx2) exacerbates Python’s inability to install multiple versions of the same dependency, leading to a ‘miserable’ transition period similar to the Pydantic V1-to-V2 leap.

  5. manueltgomes.com — httpx2 ecosystem impacthttps://manueltgomes.com/python/pydantic-httpx2-whats-new-and-how-to-take-proper-advantage-of-it/

    A major conflict occurred when the Anthropic Python SDK v1.0 migrated to httpx2, causing immediate runtime crashes for users of Pydantic AI (v2.32 and earlier) who had not yet updated to support the new client.

  6. securityarsenal.com — LLM 0.33 dependency-churn security reviewhttps://securityarsenal.com/blog/llm-033-dependency-overhaul-what-security-teams-must-verify-before-upgrading-simon-willisons-llm-cli

    Security researchers noted that such ‘rapid dependency churn’ is a significant change-control trigger, as dependency swaps are a common vector for supply-chain attacks.

  7. Farid Zakaria blog — motivation / sqlelf lineagehttps://fzakaria.com/2026/08/23/your-executable-is-a-sqlite-database

    ELF is essentially a database that ‘refuses to admit it’; sqlelf put a SQL view over ELF, and SELF inverts the relationship so the rows are the format — patching becomes UPDATE, stripping becomes DELETE + VACUUM.

    2
  8. selfdb README — tooling as SQLhttps://github.com/fzakaria/selfdb/blob/main/README.md

    ldd is a view that joins the symbols table with segments to find required shared libraries, while strip is implemented as a simple DELETE transaction followed by a VACUUM; a proof-of-concept dynamic linker self-ld performs symbol resolution and relocation patching entirely through SQL queries.

    2
  9. fzakaria/selfdb GitHub — benchmarkshttps://github.com/fzakaria/selfdb

    Packing an entire Linux userland (723 executables and 400 libraries) into one database reduced total size from 644.4 MiB to 611.9 MiB due to natural deduplication of symbols and strings; a stripped single SELF is within 1% of the original ELF (1.79 MB vs 1.76 MB).

    2 3
  10. Farid Zakaria — ‘Your executable is a SQLite database’ (author blog)https://fzakaria.com/2026/08/23/your-executable-is-a-sqlite-database

    Two processes running the same SELF binary do not share text pages the way a normally-mmap’d ELF does, because the bytes are copied out of the b-tree rather than mapped.

  11. Tildes.net discussion (~comp)https://tildes.net/~comp/1vqt/your_executable_is_a_sqlite_database

    SELF binaries copy data out of the SQLite B-tree into memory… system monitors like ps and top mistakenly display the interpreter (self-exec) as the running process rather than the application itself.

  12. LWN — kernel developer commentary on binfmt_mischttps://lwn.net/Articles/1087432/

    Manipulating kernel paths to facilitate custom loaders is ‘ripe for malicious loader injection attacks.’

Jack Sun

Jack Sun, writing.

Engineer · Bay Area

Hands-on with agentic AI all day — building frameworks, reading what industry ships, occasionally writing them down.

Digest
All · AI Tech · AI Research · AI News
Writing
Essays
Elsewhere
Subscribe
All · AI Tech · AI Research · AI News · Essays

© 2026 Wei (Jack) Sun · jacksunwei.me Built on Astro · hosted on Cloudflare