micro/go-micro · error
stream API error (%s): %s
Error message
stream API error (%s): %s
What it means
Returned by Provider.Stream when Anthropic responds with a non-200 status to a streaming request. The error message embeds the HTTP status line and the raw response body, which contains Anthropic's JSON error payload describing the actual failure (invalid API key, bad model name, rate limiting, etc.).
Source
Thrown at ai/anthropic/anthropic.go:268
apiURL := strings.TrimRight(p.opts.BaseURL, "/") + "/v1/messages"
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(reqBody))
if err != nil {
return nil, fmt.Errorf("failed to create stream request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Accept", "text/event-stream")
httpReq.Header.Set("x-api-key", p.opts.APIKey)
httpReq.Header.Set("anthropic-version", "2023-06-01")
httpResp, err := http.DefaultClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("stream API request failed: %w", err)
}
if httpResp.StatusCode != http.StatusOK {
defer httpResp.Body.Close()
respBody, _ := io.ReadAll(httpResp.Body)
return nil, fmt.Errorf("stream API error (%s): %s", httpResp.Status, string(respBody))
}
return &streamReader{body: httpResp.Body, scanner: bufio.NewScanner(httpResp.Body)}, nil
}
type streamReader struct {
body io.ReadCloser
scanner *bufio.Scanner
closed bool
}
func (s *streamReader) Recv() (*ai.Response, error) {
for s.scanner.Scan() {
line := strings.TrimSpace(s.scanner.Text())
if line == "" || strings.HasPrefix(line, ":") || strings.HasPrefix(line, "event:") {
continue
}
if !strings.HasPrefix(line, "data:") {
continueView on GitHub (pinned to 24529f1404)
Solutions
- Read the body embedded in the error message — it contains Anthropic's error.type and message pinpointing the cause.
- Verify ANTHROPIC_API_KEY is valid: curl https://api.anthropic.com/v1/messages with the key and a minimal payload.
- Confirm the model ID in the request matches an available Anthropic model and that max_tokens is set (required by the API).
- Add retry with backoff for 429/5xx; check response headers retry-after for rate limits.
Example fix
// before
resp, err := provider.Stream(ctx, req)
if err != nil { return err }
// after
resp, err := provider.Stream(ctx, req)
if err != nil {
var httpErr *ai.HTTPError
if errors.As(err, &httpErr) && httpErr.StatusCode == 429 {
time.Sleep(retryAfterBackoff(httpErr))
resp, err = provider.Stream(ctx, req)
}
} Defensive patterns
Strategy: retry
Validate before calling
// before calling: validate key presence and model
func validateOpts(key, model string) error {
if key == "" { return errors.New("missing ANTHROPIC_API_KEY") }
if !strings.HasPrefix(model, "claude-") { return errors.New("suspicious model id: "+model) }
return nil
} Type guard
func isAnthropicHTTPStatusErr(err error) (status string, body string, ok bool) {
if err == nil { return };
re := regexp.MustCompile(`stream API error \((\d{3}[^)]*)\): (.*)`)
m := re.FindStringSubmatch(err.Error())
if m == nil { return }
return m[1], m[2], true
} Try / catch
resp, err := provider.Stream(ctx, req)
if err != nil {
if status, body, ok := isAnthropicHTTPStatusErr(err); ok {
switch {
case strings.HasPrefix(status, "429"), strings.HasPrefix(status, "5"):
// retry with backoff
case strings.HasPrefix(status, "4"):
// permanent: log body (contains Anthropic error.type), fail fast
}
}
} Prevention
- Keep ANTHROPIC_API_KEY fresh and scoped
- Pin a known-good anthropic-version
- Implement exponential backoff for 429/5xx
- Read the error body — Anthropic includes error.type and message
When it happens
Trigger: Any non-200 from POST /v1/messages with stream=true: 401 invalid x-api-key, 400 invalid request (bad model, missing max_tokens), 429 rate limited, 5xx Anthropic outage.
Common situations: Expired or revoked ANTHROPIC_API_KEY, wrong API version header mismatch, requesting a model name that doesn't exist (e.g. typo in claude model ID), hitting org rate limits, or pointing BaseURL at a non-Anthropic-compatible proxy.
Related errors
- failed to create stream request: %w
- stream API request failed: %w
- stream API error (%s): %s
- API error (%s): %s
- ${rsp.Status}: ${body} (dynamic HTTP status and body)
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/b903c2b0d2a354f2.
Report an issue: GitHub.