wavetermdev/waveterm · error
anthropic %s: %s
Error message
anthropic %s: %s
What it means
parseAnthropicHTTPError converts a non-2xx HTTP response into an error. When the response body parses as Anthropic's canonical error format ({"error":{"type":...,"message":...}}) it produces "anthropic <status>: <message>", sanitized of internal hostnames. This is the highest-fidelity error path — the message is the API's own explanation.
Source
Thrown at pkg/aiusechat/anthropic/anthropic-backend.go:403
// ---------- Public entrypoint ----------
//
// Mapping rules recap (Anthropic → AI‑SDK):
// - message_start → AiMsgStart + AiMsgStartStep
// - content_block_start(type=text) → AiMsgTextStart; text_delta → AiMsgTextDelta; content_block_stop → AiMsgTextEnd
// - content_block_start(type=thinking) → AiMsgReasoningStart; thinking_delta → AiMsgReasoningDelta; stop → AiMsgReasoningEnd
// - content_block_start(type=tool_use) → AiMsgToolInputStart; input_json_delta → AiMsgToolInputDelta; stop → AiMsgToolInputAvailable
// - If final stop_reason == "tool_use": emit AiMsgFinishStep and return StopReason{Kind:ToolUse, ...} WITHOUT AiMsgFinish
// - If message_stop with stop_reason == "end_turn" or nil: emit AiMsgFinish then [DONE]
// - On Anthropic error event: AiMsgError and return StopKindError. :contentReference[oaicite:9]{index=9} :contentReference[oaicite:10]{index=10}
// parseAnthropicHTTPError parses Anthropic API HTTP error responses
func parseAnthropicHTTPError(resp *http.Response) error {
slurp, _ := io.ReadAll(resp.Body)
// Try to parse as Anthropic error format first
var eresp anthropicHTTPErrorResponse
if err := json.Unmarshal(slurp, &eresp); err == nil && eresp.Error.Message != "" {
return sanitizeHostnameInError(fmt.Errorf("anthropic %s: %s", resp.Status, eresp.Error.Message))
}
// Try to parse as proxy error format
var proxyErr uctypes.ProxyErrorResponse
if err := json.Unmarshal(slurp, &proxyErr); err == nil && !proxyErr.Success && proxyErr.Error != "" {
return sanitizeHostnameInError(fmt.Errorf("anthropic %s: %s", resp.Status, proxyErr.Error))
}
// Fall back to truncated raw response
msg := utilfn.TruncateString(strings.TrimSpace(string(slurp)), 120)
if msg == "" {
msg = "unknown error"
}
return sanitizeHostnameInError(fmt.Errorf("anthropic %s: %s", resp.Status, msg))
}
func RunAnthropicChatStep(
ctx context.Context,View on GitHub (pinned to a4447c1563)
Solutions
- Read the message after the status code — Anthropic's error.message states the exact problem (auth, quota, model name, etc.).
- For 401: rotate/fix the API key in configuration.
- For 429: retry with exponential backoff honoring rate-limit headers (RateLimitInfo is tracked by this client).
- For 529/500/overloaded: retry after a delay; these are transient upstream issues.
- For 400: fix the request (model name, max_tokens vs thinking budget, tool schemas).
Example fix
// handling pattern
stop, _, _, err := RunAnthropicChatStep(ctx, sse, opts, cont)
if err != nil {
var apiErr *apiError
if strings.Contains(err.Error(), "429") {
time.Sleep(backoff) ; retry(ctx)
}
} Defensive patterns
Strategy: retry
Validate before calling
// pre-flight: ensure key/model are set before calling
if apiKey == "" { return errors.New("missing ANTHROPIC API key") }
if !validModelName(model) { return fmt.Errorf("unknown model %q", model) } Try / catch
err := runAnthropic(ctx)
if err != nil {
switch {
case strings.Contains(err.Error(), "401"):
return refreshAPIKey(ctx)
case strings.Contains(err.Error(), "429"):
return retryWithBackoff(ctx, honorRateLimitInfo)
case strings.Contains(err.Error(), "529"), strings.Contains(err.Error(), "overloaded"):
return retryWithBackoff(ctx, nil)
default:
return err
}
} Prevention
- Validate API key and model name before requests
- Implement exponential backoff honoring RateLimitInfo for 429
- Handle 529/overloaded as transient with retries
- Keep max_tokens and thinking budgets within API limits to avoid 400s
When it happens
Trigger: Anthropic API (or proxy acting as Anthropic) returns non-2xx with body {"error":{"message":"..."}} — e.g. 401 invalid_api_key, 400 invalid_request_error, 429 rate_limit_error, 529 overloaded_error.
Common situations: Expired/invalid API keys (401); malformed requests or unsupported model names (400); rate limits (429); Anthropic capacity outages (529/500); billing issues.
Related errors
- missing zone file info for ${zoneId}:${fileName}
- call ${methodName} failed: ${resp.status} ${resp.statusText}
- Failed to fetch config: ${configResponse.statusText}
- Failed to fetch data: ${dataResponse.statusText}
- HTTP ${response.status}: ${response.statusText}
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/57550dacfa273b00.
Report an issue: GitHub.