Note: this issue was drafted by Claude via back-and-forth with @njbrake. The reasoning and decisions are his; the prose is Claude's.
Summary
All four language SDKs (Python, TS, Rust, Go) wrap the generated _client core with a hand-written shell. On the inference path the shell maps HTTP errors to the SDK's typed error; on the control-plane path (keys, users, budgets, pricing, usage) it does not, so management calls leak the raw generated/transport error. The same bug exists identically in all four shells, which is the kind of drift a hand-mirrored-shell architecture invites: the "control-plane must map errors like inference does" convention lives in four repos with no shared enforcement.
This issue is about preventing recurrence, not the per-SDK fixes (tracked separately, see below).
Why codegen cannot fix this
otari-sdk-codegen.yml regenerates only the typed _client core and syncs it into each SDK's core directory. Its own header says the hand-written shell (streaming, typed error mapping, ergonomic methods) "is not regenerated here." The bug lives in that shell, so there is no generator change that addresses it. Enforcement has to live in the cross-SDK test contract.
Proposal: a cross-SDK conformance assertion
tests/integration/README.md already defines a cross-SDK control-plane test shape ("the same shape applies to the TS / Go / Rust SDKs"). Add one assertion to that contract, implemented in each SDK's integration suite:
A control-plane call made with an invalid master key surfaces the SDK's typed authentication error (the same type the inference path raises on a 401), not the raw generated/transport error type.
Optionally broaden to a couple more statuses (404, 429) to assert the control-plane and inference paths share one error contract. With this in CI, any SDK whose control-plane stops mapping errors fails the contract, so the four shells cannot silently diverge again.
Per-SDK fixes (separate)
This issue tracks the conformance assertion that keeps them aligned.
Note: this issue was drafted by Claude via back-and-forth with @njbrake. The reasoning and decisions are his; the prose is Claude's.
Summary
All four language SDKs (Python, TS, Rust, Go) wrap the generated
_clientcore with a hand-written shell. On the inference path the shell maps HTTP errors to the SDK's typed error; on the control-plane path (keys, users, budgets, pricing, usage) it does not, so management calls leak the raw generated/transport error. The same bug exists identically in all four shells, which is the kind of drift a hand-mirrored-shell architecture invites: the "control-plane must map errors like inference does" convention lives in four repos with no shared enforcement.This issue is about preventing recurrence, not the per-SDK fixes (tracked separately, see below).
Why codegen cannot fix this
otari-sdk-codegen.ymlregenerates only the typed_clientcore and syncs it into each SDK's core directory. Its own header says the hand-written shell (streaming, typed error mapping, ergonomic methods) "is not regenerated here." The bug lives in that shell, so there is no generator change that addresses it. Enforcement has to live in the cross-SDK test contract.Proposal: a cross-SDK conformance assertion
tests/integration/README.mdalready defines a cross-SDK control-plane test shape ("the same shape applies to the TS / Go / Rust SDKs"). Add one assertion to that contract, implemented in each SDK's integration suite:Optionally broaden to a couple more statuses (404, 429) to assert the control-plane and inference paths share one error contract. With this in CI, any SDK whose control-plane stops mapping errors fails the contract, so the four shells cannot silently diverge again.
Per-SDK fixes (separate)
This issue tracks the conformance assertion that keeps them aligned.