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:
| Error | Typical origin | Default failover? |
|---|---|---|
RateLimitError | 429 | yes |
TimeoutError | network timeout | yes |
ConnectionError | connection failure | yes |
ServerError | 5xx | yes |
NotFoundError | 404 (model/endpoint) | yes (no retry) |
OutputValidationError | parse() with off-schema JSON | yes (no retry) |
AuthError | 401/403 | no |
BadRequestError | 400 (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 includeauthorbad_requestby 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 →
APITimeoutErrorand 409 →ServerError(both transient, retried); 413 →BadRequestError. - Refusals and empty responses become normalized errors. A response without
choices, an OpenAI refusal inparse(parsed=None) or Claude withouttool_useinparseraiseOutputValidationError/ServerError— which go into failover — instead of rawIndexError/StopIteration. - Truncated output in OpenAI
parse(LengthFinishReasonError) becomesTruncatedError, with the hint to raisemax_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): withrequest=True, a PydanticValidationErrorraised while building the request (SDKs like google-genai and mistralai validate requests) becomesBadRequestError, notOutputValidationError— an invalidextra=no longer fails over to another model. The adapters already use it outsideparse.
Prompt registry
Version prompts (history, production tag, rollback without deploy) and reference them by name with PromptVersion.pull/push. Opt-in: coexists with prompts in code; resolves to a plain string compatible with templates, structured output and tools.
Retry and fallback
jangada combines two defenses against API failures: retry with backoff on the same candidate and fallback to another model/provider.