Report a short provider error code when a model call fails

QU
15d ago
+63-3

Cause

When the model API returns an error, post_json() only says Model provider returned HTTP 400; no action executed. A TypeSafe rejection for request size (max_tokens_exceeded) and an invalid key produce the same message, so users cannot tell why the call failed. A connection failure only says Model connection failed, so a timeout looks the same as a refused connection.

Changes

  • The error code is read from the JSON error body and appended, as in HTTP 400 (max_tokens_exceeded). Only two places are read: TypeSafe's detail.error_type (the real response is {"detail":{"error_type":"max_tokens_exceeded"}}) and the OpenAI-style error.code (falling back to error.type) used for text generation. A value is used only when it looks like a short identifier ([A-Za-z0-9_.:-]{1,100}). The response body, message and credentials are never shown. Message-only responses such as {"detail":"Too many choices. …"} and non-JSON responses keep only the HTTP status, as before.
  • A connection failure now names the exception type, such as ReadTimeout. The exception message is still not chained (from None).
  • One sentence added to the Development section of the README.

#37 also changes the error handling in post_json() (it keeps the response body on the exception). This PR only adds a short code to the message, so whichever lands first, the rebase of the other is small.

Tests

  • 11 tests added: the TypeSafe code, message not shown, a message-only detail, an unlisted detail.code not shown, the OpenAI code and the fallback from a numeric code to type, rejecting values with spaces and unstructured errors, non-JSON responses, and a ReadTimeout that shows its type but not its message and is not chained.
  • uv run ruff check . and uv run pytest pass on macOS, Linux and Windows 11. On Windows they were run with PYTHONUTF8=1 because of the known cp932 failure when upstream main reads snapshot.js (#5). The follow-up commit a5ef60c (reading only detail.error_type, from the review below) was tested on Linux: 42 passed.
  • Real requests to TypeSafe (/v1/systemone) through post_json() show:
    • Invalid key: Model provider returned HTTP 401 (authentication_error); no action executed.
    • A request of about 570 KB: Model provider returned HTTP 400 (max_tokens_exceeded); no action executed.

🤖 Generated with Claude Code


Summary by cubic

Model API failures now report a short provider error code (e.g. max_tokens_exceeded) alongside the HTTP status, and connection failures report the exception type (e.g. ReadTimeout), so users can tell why a call failed.

  • Reads the code from TypeSafe's detail.error_type or an OpenAI-style error.code/error.type, only when it is a short identifier.
  • Never includes the response body, message, or credentials; non-JSON and message-only responses keep just the HTTP status. Connection failure details are not chained.
  • Adds 11 tests covering code extraction, sanitization, non-JSON responses, and timeout handling, plus a note in the README's Development section.