Liking cljdoc? Tell your friends :D

com.blockether.vis.internal.provider-error

Single source of truth for provider-error presentation.

Typed provider-error content and per-iteration trace rows derive their wording and facts from this namespace, so a failure reads identically everywhere.

err is the error map carried on a trace entry / ex-info: {:message .. :data {:status .. :body .. :request-id ..} ..}. Every helper tolerates the bare ex-info shape too (via ex-message).

CLASSIFICATION IS SVAR'S. svar-classification wraps svar.internal.failure/classify — the single owner of failure families, retry safety and :reached-model? for everything svar transports. This namespace owns WORDING, plus the handful of failures svar cannot see (its typed empty-content and stream-watchdog outcomes, gateway tool-field rejections, tool-schema defects). Never grow a second copy of svar's heuristics here.

Single source of truth for provider-error presentation.

Typed provider-error content and per-iteration trace rows derive their wording
and facts from this namespace, so a failure reads identically everywhere.

`err` is the error map carried on a trace entry / ex-info:
`{:message .. :data {:status .. :body .. :request-id ..} ..}`. Every
helper tolerates the bare ex-info shape too (via `ex-message`).

CLASSIFICATION IS SVAR'S. `svar-classification` wraps
`svar.internal.failure/classify` — the single owner of failure families,
retry safety and `:reached-model?` for everything svar transports. This
namespace owns WORDING, plus the handful of failures svar cannot see (its
typed empty-content and stream-watchdog outcomes, gateway tool-field
rejections, tool-schema defects). Never grow a second copy of svar's
heuristics here.
raw docstring

attempt->lineclj

(attempt->line {:keys [provider model status reason]})

One attempt → a compact provider/model: <status> <reason> line, e.g. anthropic/claude-opus-4: 429 rate-limit.

One attempt → a compact `provider/model: <status> <reason>` line, e.g.
`anthropic/claude-opus-4: 429 rate-limit`.
sourceraw docstring

auth-exhausted-attempts?clj

(auth-exhausted-attempts? err)

True when EVERY provider svar tried bowed out on credentials.

The all-providers-exhausted / Provider unavailable wrapper carries no status and no provider prose of its own, so auth-provider-error? — which reads only the wrapper — called an all-401 fleet failure :generic and told the user to retry, or switch provider/model. An unchanged retry re-sends the SAME rejected key to every provider. The evidence is on the attempts, so read it there; with no attempts recorded there is no auth verdict to make.

True when EVERY provider svar tried bowed out on credentials.

The `all-providers-exhausted` / `Provider unavailable` wrapper carries no
status and no provider prose of its own, so `auth-provider-error?` — which
reads only the wrapper — called an all-401 fleet failure `:generic` and told
the user to `retry, or switch provider/model`. An unchanged retry re-sends
the SAME rejected key to every provider. The evidence is on the attempts, so
read it there; with no attempts recorded there is no auth verdict to make.
sourceraw docstring

auth-failed-provider-idsclj

(auth-failed-provider-ids err)

The providers that bowed out on credentials, in the order svar tried them.

The providers that bowed out on credentials, in the order svar tried them.
sourceraw docstring

auth-provider-error?clj

(auth-provider-error? status message wrapper-message)
source

auth-provider-next-stepclj

(auth-provider-next-step data)
source

context-overflow-error?clj

(context-overflow-error? err)

True only for a canonical typed context-window failure.

True only for a canonical typed context-window failure.
sourceraw docstring

CONTEXT_OVERFLOW_TYPESclj

Every typed context-window failure svar can raise.

:svar.tokens/context-overflow comes from the token-budget checker (router/check-context) and from the streamed context_length_exceeded family. :svar.core/context-overflow is what the ask-code! PREFLIGHT guard throws — and that is the path VIS actually takes, so it must be recognized too. Matching only the tokens variant made a preflight overflow miss EVERY overflow handler (emergency fold, terminal breaker, error card) and surface as a bogus Provider unavailable, with the error fed back into a request that could never reach the model.

Every typed context-window failure svar can raise.

`:svar.tokens/context-overflow` comes from the token-budget checker
(`router/check-context`) and from the streamed `context_length_exceeded`
family. `:svar.core/context-overflow` is what the `ask-code!` PREFLIGHT
guard throws — and that is the path VIS actually takes, so it must be
recognized too. Matching only the `tokens` variant made a preflight
overflow miss EVERY overflow handler (emergency fold, terminal breaker,
error card) and surface as a bogus `Provider unavailable`, with the error
fed back into a request that could never reach the model.
sourceraw docstring

empty-content-error?clj

(empty-content-error? err)

True when the failure is svar's TYPED :svar.llm/empty-content — the provider ACCEPTED the request and answered a normal HTTP-200 stream that carried no text and no tool call. Dispatches on the typed ex-data (attached verbatim under :data on trace-entry error maps) — NEVER on message text.

Emphatically NOT a rejection: the model ran and emitted nothing. Presenting it as 'the provider rejected the request' is false and points the user at auth/quota — the wrong place.

True when the failure is svar's TYPED `:svar.llm/empty-content` — the
provider ACCEPTED the request and answered a normal HTTP-200 stream that
carried no text and no tool call. Dispatches on the typed `ex-data`
(attached verbatim under `:data` on trace-entry error maps) — NEVER on
message text.

Emphatically NOT a rejection: the model ran and emitted nothing. Presenting
it as 'the provider rejected the request' is false and points the user at
auth/quota — the wrong place.
sourceraw docstring

gateway-injected-tool-fieldclj

(gateway-injected-tool-field message)

The tool-payload FIELD an OpenAI-compatible gateway added on Vis' behalf and the upstream then rejected, lower-cased — or nil.

Vis never emits strict nor a tool-level additionalProperties: LiteLLM forwards them into the Bedrock Converse toolSpec while translating our tool defs, and Bedrock's Anthropic-compatible validator answers tools.0.custom.strict: Extra inputs are not permitted. So this is NOT a defect in a Vis tool schema — no schema edit can avoid it, and an unchanged retry fails identically.

The tool-payload FIELD an OpenAI-compatible gateway added on Vis' behalf and
the upstream then rejected, lower-cased — or nil.

Vis never emits `strict` nor a tool-level `additionalProperties`: LiteLLM
forwards them into the Bedrock Converse `toolSpec` while translating our tool
defs, and Bedrock's Anthropic-compatible validator answers
`tools.0.custom.strict: Extra inputs are not permitted`. So this is NOT a
defect in a Vis tool schema — no schema edit can avoid it, and an unchanged
retry fails identically.
sourceraw docstring

gateway-tool-field-rejectionclj

(gateway-tool-field-rejection err)

gateway-injected-tool-field over an error's own body + message.

`gateway-injected-tool-field` over an error's own body + message.
sourceraw docstring

generic-wrapper-message?clj

(generic-wrapper-message? message)

True when the svar wrapper message is just a status echo (e.g. Exceptional status code: 400) that adds nothing beyond the HTTP row.

True when the svar wrapper message is just a status echo (e.g.
`Exceptional status code: 400`) that adds nothing beyond the HTTP row.
sourceraw docstring

invalid-thinking-signature-message?clj

(invalid-thinking-signature-message? message)
source

output-budget-minimumclj

(output-budget-minimum message)

The minimum output-token count the provider NAMED in its rejection, when it named one (Expected a value >= 16, must be at least 16). nil otherwise.

The minimum output-token count the provider NAMED in its rejection, when it
named one (`Expected a value >= 16`, `must be at least 16`). nil otherwise.
sourceraw docstring

output-budget-too-small-error?clj

(output-budget-too-small-error? status message)

True when a provider rejected the request because the OUTPUT-token budget it was given is below that model's minimum — e.g. gpt-5.6-terra 400s on max_output_tokens: 8 but accepts 16. Deterministic like a schema defect: an unchanged retry fails identically, raising the configured floor fixes it.

True when a provider rejected the request because the OUTPUT-token budget it
was given is below that model's minimum — e.g. `gpt-5.6-terra` 400s on
`max_output_tokens: 8` but accepts `16`. Deterministic like a schema defect:
an unchanged retry fails identically, raising the configured floor fixes it.
sourceraw docstring

parse-provider-bodyclj

(parse-provider-body body)
source

provider-body-messageclj

(provider-body-message body)
source

provider-error-attemptsclj

(provider-error-attempts err)

The per-provider failure records svar accumulates on an all-providers-exhausted error — [{:provider :model :status :reason :error} …], one per provider tried. Empty when svar didn't attach them (older svar / a non-routing failure).

Read ex-data too, not only a trace entry's :data: svar's router throws a LIVE ex-info whose message is just Provider unavailable and keeps the real cause (litellm.Timeout: BedrockException: Timeout Error …) on :attempts in its ex-data. Missing that reduced every routed failure to the generic card on the surface that presents the throwable itself.

The per-provider failure records svar accumulates on an `all-providers-exhausted`
error — `[{:provider :model :status :reason :error} …]`, one per provider tried.
Empty when svar didn't attach them (older svar / a non-routing failure).

Read `ex-data` too, not only a trace entry's `:data`: svar's router throws a
LIVE `ex-info` whose message is just `Provider unavailable` and keeps the real
cause (`litellm.Timeout: BedrockException: Timeout Error …`) on `:attempts` in
its `ex-data`. Missing that reduced every routed failure to the generic card
on the surface that presents the throwable itself.
sourceraw docstring

provider-error-attempts-summaryclj

(provider-error-attempts-summary err)

The attempts joined into ONE scannable line (a: 429 rate-limit · b: 401 auth), or nil when there are none. The at-a-glance 'why each provider bowed out'.

The attempts joined into ONE scannable line (`a: 429 rate-limit · b: 401 auth`),
or nil when there are none. The at-a-glance 'why each provider bowed out'.
sourceraw docstring

provider-error-contentclj

(provider-error-content err)

Canonical typed content for a provider failure. The error remains data; channels decide how to present it and Markdown is not used as an envelope.

Canonical typed content for a provider failure. The error remains data;
channels decide how to present it and Markdown is not used as an envelope.
sourceraw docstring

provider-error-explanationclj

(provider-error-explanation err)

The WHAT HAPPENED: prose line — the single canonical human sentence for this failure, shared by every surface. The actionable step lives in provider-error-next-step (a separate block), so this is JUST the diagnosis.

The `WHAT HAPPENED:` prose line — the single canonical human sentence for this
failure, shared by every surface. The actionable step lives in
`provider-error-next-step` (a separate block), so this is JUST the diagnosis.
sourceraw docstring

provider-error-factsclj

(provider-error-facts err)

Ordered [label value] rows of the bare facts (no prose). Same set the IR renders as a <ul> and the TUI renders as plain rows.

Ordered `[label value]` rows of the bare facts (no prose). Same set the
IR renders as a `<ul>` and the TUI renders as plain rows.
sourceraw docstring

provider-error-infoclj

(provider-error-info err)

Structured echo of the facts a chat surface can render compactly without parsing the IR back out.

Structured echo of the facts a chat surface can render
compactly without parsing the IR back out.
sourceraw docstring

provider-error-kindclj

(provider-error-kind err)
source

provider-error-next-stepclj

(provider-error-next-step err)

The actionable NEXT STEP: line — what the user should DO — by kind. Kept SEPARATE from the diagnosis so surfaces can make it prominent.

The actionable `NEXT STEP:` line — what the user should DO — by kind. Kept
SEPARATE from the diagnosis so surfaces can make it prominent.
sourceraw docstring

provider-error-raw-bodyclj

(provider-error-raw-body err)

Truncated raw upstream body — surfaced ONLY when no structured provider message could be extracted (HTML pages, bare 5xx). nil otherwise so the readable message isn't echoed twice.

Truncated raw upstream body — surfaced ONLY when no structured provider
message could be extracted (HTML pages, bare 5xx). nil otherwise so the
readable message isn't echoed twice.
sourceraw docstring

provider-error-retryable?clj

(provider-error-retryable? err)

Whether retrying the SAME request can plausibly succeed. Surfaces read this so they never suggest a blind retry for a terminal or pinned failure (auth, billing, tool schema, resource mismatch, context overflow).

Vis answers only for the kinds it classifies better than svar does (its typed stream watchdogs, empty content, gateway tool-field rejections). Everything else — every plain HTTP failure — defers to svar-classification's :retryable?, so vis never keeps a second, drifting copy of that verdict.

This is presentation only: svar owns the actual retry ladder.

Whether retrying the SAME request can plausibly succeed. Surfaces read this
so they never suggest a blind retry for a terminal or pinned failure (auth,
billing, tool schema, resource mismatch, context overflow).

Vis answers only for the kinds it classifies better than svar does (its typed
stream watchdogs, empty content, gateway tool-field rejections). Everything
else — every plain HTTP failure — defers to `svar-classification`'s
`:retryable?`, so vis never keeps a second, drifting copy of that verdict.

This is presentation only: svar owns the actual retry ladder.
sourceraw docstring

provider-error-titleclj

(provider-error-title err)

A SHORT headline for the failure, by kind — the card title on every surface.

A SHORT headline for the failure, by kind — the card title on every surface.
sourceraw docstring

provider-error-upstream-textclj

(provider-error-upstream-text err)

EVERY scrap of upstream failure text Vis holds for err, lowercased and joined: the wrapper message, the raw upstream body, and the per-provider attempt reasons/errors svar records on a routing failure.

Classification must read THIS and not only the wrapper message. An all-providers-exhausted / Provider unavailable wrapper keeps the real cause (litellm.Timeout: BedrockException: Timeout Error …) down on the attempts — which is exactly how an upstream timeout used to be presented as a generic outage.

EVERY scrap of upstream failure text Vis holds for `err`, lowercased and
joined: the wrapper message, the raw upstream body, and the per-provider
attempt reasons/errors svar records on a routing failure.

Classification must read THIS and not only the wrapper message. An
`all-providers-exhausted` / `Provider unavailable` wrapper keeps the real
cause (`litellm.Timeout: BedrockException: Timeout Error …`) down on the
attempts — which is exactly how an upstream timeout used to be presented as
a generic outage.
sourceraw docstring

provider-failure?clj

(provider-failure? err)

True when err is a PROVIDER failure — presentable as the styled card provider-error-content builds — rather than an internal Vis bug whose bare message is all there is. Either it classifies to a KNOWN kind, or it carries provider evidence: an upstream HTTP status, a routing trace, a provider id.

True when `err` is a PROVIDER failure — presentable as the styled card
`provider-error-content` builds — rather than an internal Vis bug whose bare
message is all there is. Either it classifies to a KNOWN kind, or it carries
provider evidence: an upstream HTTP status, a routing trace, a provider id.
sourceraw docstring

provider-structured-messageclj

(provider-structured-message body)

Human-readable message a provider put in its error envelope, if any. Covers Anthropic/OpenAI {:error {:message}}, bare {:message}, and Codex/ChatGPT {:detail}. nil when the body has no message field (HTML page, bare 5xx) — the caller then surfaces the raw body instead.

Human-readable message a provider put in its error envelope, if any.
Covers Anthropic/OpenAI `{:error {:message}}`, bare `{:message}`, and
Codex/ChatGPT `{:detail}`. nil when the body has no message field (HTML
page, bare 5xx) — the caller then surfaces the raw body instead.
sourceraw docstring

resource-mismatch-error?clj

(resource-mismatch-error? text)

True when the conversation/item is PINNED to a different backend resource — Azure OpenAI's 'The requested item was created under a different Azure OpenAI resource', a stored response read from the wrong deployment. Emphatically not transient: the identical request against the identical endpoint fails the identical way, so suggesting a blind retry is wrong.

True when the conversation/item is PINNED to a different backend resource —
Azure OpenAI's 'The requested item was created under a different Azure OpenAI
resource', a stored response read from the wrong deployment. Emphatically not
transient: the identical request against the identical endpoint fails the
identical way, so suggesting a blind retry is wrong.
sourceraw docstring

split-error-labelclj

(split-error-label s)

Split a provider-error prose line into [label body] at the first :.

provider-error-explanation / provider-error-next-step always lead with an ALL-CAPS label (WHAT HAPPENED: / NEXT STEP:). This returns ["<LABEL>: " "<body sentence>"] so a surface can render the label distinctly (bold in final prose, SGR/bold sentinels in the TUI trace) while the body stays plain — ONE convention, shared by every surface, so the label/body split never diverges between the IR and the TUI recap.

Returns [nil s] (whole string as the body, no label) when s does not follow the ALL-CAPS: convention, so callers can treat the label as optional without a separate nil-check.

Split a provider-error prose line into `[label body]` at the first `: `.

`provider-error-explanation` / `provider-error-next-step` always lead with
an ALL-CAPS label (`WHAT HAPPENED: ` / `NEXT STEP: `). This returns
`["<LABEL>: " "<body sentence>"]` so a surface can render the label
distinctly (bold in final prose, SGR/bold sentinels in the TUI trace)
while the body stays plain — ONE convention, shared by every surface, so
the label/body split never diverges between the IR and the TUI recap.

Returns `[nil s]` (whole string as the body, no label) when `s` does not
follow the `ALL-CAPS:` convention, so callers can treat the label as
optional without a separate nil-check.
sourceraw docstring

stream-timeout-error?clj

(stream-timeout-error? err)

True when the failure is one of svar's TYPED stream watchdogs firing: :svar.core/stream-semantic-timeout (transport alive, zero model/progress events inside the budget) or :svar.core/stream-idle-timeout (no bytes at all). Dispatches on typed ex-data — NEVER on message text.

Emphatically NOT a rejection and NOT an outage: the provider accepted the request, the model was (as far as anyone knows) still working, and VIS hung up. Presenting it as 'the provider rejected the request before the model ran' is false and points the user at auth/quota — the wrong place.

True when the failure is one of svar's TYPED stream watchdogs firing:
`:svar.core/stream-semantic-timeout` (transport alive, zero model/progress
events inside the budget) or `:svar.core/stream-idle-timeout` (no bytes at
all). Dispatches on typed `ex-data` — NEVER on message text.

Emphatically NOT a rejection and NOT an outage: the provider accepted the
request, the model was (as far as anyone knows) still working, and VIS hung
up. Presenting it as 'the provider rejected the request before the model
ran' is false and points the user at auth/quota — the wrong place.
sourceraw docstring

svar-classificationclj

(svar-classification err)

svar's canonical verdict for err: {:category :retryable? :reached-model? :request-id :status …} from svar.internal.failure/classify.

Vis presents, svar classifies. Reading this instead of re-implementing it is what keeps the two layers from disagreeing about whether the model ever saw the request — and svar, not vis, owns the retry ladder that follows.

svar's canonical verdict for `err`: `{:category :retryable? :reached-model?
:request-id :status …}` from `svar.internal.failure/classify`.

Vis presents, svar classifies. Reading this instead of re-implementing it is
what keeps the two layers from disagreeing about whether the model ever saw
the request — and svar, not vis, owns the retry ladder that follows.
sourceraw docstring

tool-schema-rejection-message?clj

(tool-schema-rejection-message? message)

True when a provider rejected a native tool because its input-schema root uses a JSON Schema union that provider tool APIs forbid.

True when a provider rejected a native tool because its input-schema root
uses a JSON Schema union that provider tool APIs forbid.
sourceraw docstring

transport-throwable?clj

(transport-throwable? t)

True when Throwable t is a CONNECTION/transport failure — the SAME classification transport-error? makes from a parsed provider error, but taken straight off a Throwable so the RETRY gate and the human message agree.

Walks the whole cause chain for the message text. Idempotent by definition: a transport failure means the request never reached the model (the socket closed with no bytes, was reset/refused/timed out, DNS/TLS died), so it is ALWAYS safe to retry — regardless of whether a response STREAM had started (a pre-response failure carries no :stream?, yet is the safest retry of all). A real REJECTION carries an HTTP status, so transport-error?'s nil status guard keeps 4xx/5xx out of this path.

True when Throwable `t` is a CONNECTION/transport failure — the SAME
classification `transport-error?` makes from a parsed provider error, but
taken straight off a Throwable so the RETRY gate and the human message agree.

Walks the whole cause chain for the message text. Idempotent by definition:
a transport failure means the request never reached the model (the socket
closed with no bytes, was reset/refused/timed out, DNS/TLS died), so it is
ALWAYS safe to retry — regardless of whether a response STREAM had started
(a pre-response failure carries no `:stream?`, yet is the safest retry of
all). A real REJECTION carries an HTTP status, so `transport-error?`'s
`nil status` guard keeps 4xx/5xx out of this path.
sourceraw docstring

unanswered-request?clj

(unanswered-request? err)

True when NOTHING answered: svar reports a pre-response transport drop, so :reached-model? is false and re-sending is side-effect-free.

Why ask svar rather than transport-error?: vis' own text gate only fires when NO HTTP status is present, but the shared gateway answers a pre-response socket drop with a 502 whose body carries the real cause. That status alone made vis call it a rejection — the exact complaint in issue #69. A :connect-timeout is deliberately NOT folded in here: upstream-timeout-phase already says while connecting — the request never reached the model.

True when NOTHING answered: svar reports a pre-response transport drop, so
`:reached-model?` is false and re-sending is side-effect-free.

Why ask svar rather than `transport-error?`: vis' own text gate only fires
when NO HTTP status is present, but the shared gateway answers a pre-response
socket drop with a 502 whose body carries the real cause. That status alone
made vis call it a rejection — the exact complaint in issue #69. A
`:connect-timeout` is deliberately NOT folded in here: `upstream-timeout-phase`
already says `while connecting — the request never reached the model`.
sourceraw docstring

upstream-timeout-error?clj

(upstream-timeout-error? status text)

True when the UPSTREAM request timed out: a provider/gateway/SDK deadline that arrives as message text (litellm.Timeout: BedrockException: Timeout Error) or as HTTP 408/504 — NOT svar's own typed stream watchdogs, which stream-timeout-error? already owns. Text-based on purpose: these carry no typed ex-data, only prose, and collapsing them into Provider unavailable hides both the real failure mode and the right recovery.

True when the UPSTREAM request timed out: a provider/gateway/SDK deadline that
arrives as message text (`litellm.Timeout: BedrockException: Timeout Error`)
or as HTTP 408/504 — NOT svar's own typed stream watchdogs, which
`stream-timeout-error?` already owns. Text-based on purpose: these carry no
typed `ex-data`, only prose, and collapsing them into `Provider unavailable`
hides both the real failure mode and the right recovery.
sourceraw docstring

upstream-timeout-phaseclj

(upstream-timeout-phase status text)

:connect (the request never reached the model), :read (it did, and the response never finished) or nil (the provider only said 'deadline'). The phase is the whole difference between 'safe, nothing ran' and 'the model may have started', so the NEXT STEP line depends on it.

`:connect` (the request never reached the model), `:read` (it did, and the
response never finished) or nil (the provider only said 'deadline'). The
phase is the whole difference between 'safe, nothing ran' and 'the model may
have started', so the NEXT STEP line depends on it.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close