Error handling¶
The library reports failures through two distinct surfaces, and which one you get tells you where the failure happened:
- A returned
errorfromStreamorCompletemeans the request was rejected before anything reached the provider — bad configuration, a missing API key, or an unregistered protocol. No tokens were spent. - A failed response means the request reached the provider but generation
did not complete normally.
Completereturns the partialAssistantMessagetogether with the error; a stream ends with anEventError. The message'sStopReasonisStopReasonError(orStopReasonAbortedfor cancellation) andErrorMessageholds the detail.
response, err := llm.Complete(ctx, model, input, opts)
if err != nil {
// Request never left the process, or the provider stream failed.
// response may still carry a partial message and StopReason.
log.Fatalf("request failed: %v", err)
}
Setup errors returned before sending¶
Stream and Complete validate the request and resolve credentials before
dispatching to an adapter. They return an error, without contacting the
provider, when:
- The API key is empty. After checking
StreamOptions.APIKeyand the provider's environment variables, the adapter returns a provider-aware error that names exactly which variables were checked. See below. - No adapter is registered for the model's protocol. You forgot the blank
import (
_ "github.com/ktsoator/or/llm/openai"or.../llm/anthropic, orllm/all). The error isno adapter registered for protocol "...". - The options fail validation.
StreamOptions.Validateruns first — most commonly this rejectsProtocolOptionsthat don't match the target protocol (for example passingAnthropicStreamOptionsto an OpenAI-compatible model).
Missing API key¶
When no key is found, the error names the provider and every environment variable that was checked, in precedence order:
API key is empty for provider "anthropic" (set ANTHROPIC_OAUTH_TOKEN or ANTHROPIC_API_KEY or pass StreamOptions.APIKey)
Keys are resolved in this order, first non-empty wins:
StreamOptions.APIKey, if set.StreamOptions.Env— a request-scopedProviderEnvmap, checked before the process environment. Useful for multi-tenant servers that hold each user's key in memory rather than inos.Environ.- The provider's environment variables from the process.
// Explicit key, no environment lookup.
opts := llm.StreamOptions{APIKey: userKey}
// Request-scoped environment, overrides the process env for this call only.
opts := llm.StreamOptions{Env: llm.ProviderEnv{"ANTHROPIC_API_KEY": userKey}}
To inspect key resolution yourself — for example to fail fast at startup or to show a setup hint — use the key helpers:
if len(llm.FindEnvAPIKeys(model.Provider)) == 0 {
log.Printf("no key configured; expected one of %v",
llm.APIKeyEnvVars(model.Provider))
}
APIKeyEnvVars returns the variables a provider checks, FindEnvAPIKeys
returns the ones actually set, and MissingAPIKeyError builds the same message
the library uses.
Failed and cancelled responses¶
Once the request reaches the provider, branch on StopReason rather than
treating every non-nil error as fatal. See
Reading responses for the full table; the two
error-related reasons are:
StopReasonError— a provider or runtime failure mid-stream. ReadErrorMessage; do not execute any tool calls on the message.StopReasonAborted— thecontextwas cancelled. Stop cleanly; this is expected when you cancel a request.
response, err := llm.Complete(ctx, model, input, opts)
switch response.StopReason {
case llm.StopReasonError:
log.Printf("provider error: %s", response.ErrorMessage)
case llm.StopReasonAborted:
log.Print("cancelled")
default:
fmt.Println(response.Text())
}
_ = err
When streaming, the same failure arrives as a terminal event:
for event := range events {
if event.Type == llm.EventError {
log.Printf("stream failed: %v", event.Err)
break
}
}
See Streaming § cancellation for cancelling an in-flight stream.
Context overflow¶
A request that exceeds the model's context window may fail explicitly or be
silently truncated by the provider. IsContextOverflow detects both, so you can
compact history and retry instead of surfacing a raw error:
if llm.IsContextOverflow(response, model.ContextWindow) {
// Drop or summarize old messages, then retry the turn.
}
See Reading responses § detect context overflow.
Retries and timeouts¶
Transient provider failures are retried by the underlying SDK. Tune this per
request with StreamOptions.MaxRetries (set 0 to disable) and Timeout (caps
each attempt independently of the context deadline). See
Configuration for the full option set.
Recovered, non-fatal issues¶
Not every problem is an error. Malformed or truncated tool-call arguments are
recovered best-effort and recorded in AssistantMessage.Diagnostics rather than
failing the response — always inspect diagnostics before executing a tool with
side effects. See
Reading responses § diagnostics.