Jangada AIJangada AI

Normalized errors

Each SDK raises different exceptions. jangada translates everything into a single hierarchy via errors.classify(), with status_code when available. No native SDK error escapes the adapter boundary.

from jangada_ai import LLM, errors

try:
    LLM("openai", "nonexistent-model").complete("hi")
except errors.NotFoundError as e:
    print(e.status_code)   # 404
except errors.LLMError as e:
    print("generic failure:", e)

Hierarchy (summary)

All inherit from errors.LLMError. The main categories:

ErrorTypical originDefault failover?
RateLimitError429yes
TimeoutErrornetwork timeoutyes
ConnectionErrorconnection failureyes
ServerError5xxyes
NotFoundError404 (model/endpoint)yes (no retry)
OutputValidationErrorparse() with off-schema JSONyes (no retry)
AuthError401/403no
BadRequestError400 (invalid params)no

Sets used by the policy

  • errors.TRANSIENT — what triggers retry with backoff (rate limit, timeout, connection, 5xx).
  • errors.DEFAULT_FAILOVER — what triggers fallback (the transient ones + 404 + OutputValidationError). Does not include auth or bad_request by default.

You can customize retry_on= and backoff_on= per LLM — see Retry and fallback.

What changed in 1.9.0

  • More HTTP statuses mapped: 408 → APITimeoutError and 409 → ServerError (both transient, retried); 413 → BadRequestError.
  • Refusals and empty responses become normalized errors. A response without choices, an OpenAI refusal in parse (parsed=None) or Claude without tool_use in parse raise OutputValidationError / ServerError — which go into failover — instead of raw IndexError/StopIteration.
  • Truncated output in OpenAI parse (LengthFinishReasonError) becomes TruncatedError, with the hint to raise max_tokens.
  • Gemini with a safety-blocked prompt raises BadRequestError (no retry nor fallback — it's deterministic), instead of a "transient" ServerError.
  • classify(e, provider, *, request=False): with request=True, a Pydantic ValidationError raised while building the request (SDKs like google-genai and mistralai validate requests) becomes BadRequestError, not OutputValidationError — an invalid extra= no longer fails over to another model. The adapters already use it outside parse.

On this page