micro/go-micro · error

stream API error (%s): %s

Error message

stream API error (%s): %s

What it means

Thrown in the OpenAI provider's Stream when the chat-completions endpoint responds with a non-200 HTTP status. The provider closes the body, reads it, and includes both the HTTP status (e.g. '401 Unauthorized') and the raw response body in the error so the API's JSON error message is directly visible.

Source

Thrown at ai/openai/openai.go:229

		return nil, fmt.Errorf("failed to marshal stream request: %w", err)
	}
	apiURL := strings.TrimRight(p.opts.BaseURL, "/") + "/v1/chat/completions"
	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("Authorization", "Bearer "+p.opts.APIKey)

	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 &openAIStream{body: httpResp.Body, scanner: bufio.NewScanner(httpResp.Body)}, nil
}

type openAIStream struct {
	body    io.ReadCloser
	scanner *bufio.Scanner
	closed  bool
}

func (s *openAIStream) Recv() (*ai.Response, error) {
	for s.scanner.Scan() {
		line := strings.TrimSpace(s.scanner.Text())
		if line == "" || strings.HasPrefix(line, ":") {
			continue
		}
		if !strings.HasPrefix(line, "data:") {
			continue

View on GitHub (pinned to 24529f1404)

Solutions

  1. Read status+body in the error: fix credentials if 401, wait/backoff if 429
  2. Verify the API key is set and valid (echo $OPENAI_API_KEY, check dashboard)
  3. Correct the model name / remove unsupported parameters (e.g. invalid Effort) if 400
  4. Check status.openai.com for outages if 5xx, and add retry with backoff

Example fix

// before
p, _ := NewProvider(WithAPIKey("")) // 401 Unauthorized
// after
p, _ := NewProvider(WithAPIKey(os.Getenv("OPENAI_API_KEY")))
Defensive patterns

Strategy: try-catch

Validate before calling

if apiKey == "" {
    return errors.New("OPENAI_API_KEY not set")
}
// optionally: preflight a cheap models list call to validate the key
req, _ := http.NewRequest("GET", baseURL+"/v1/models", nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil || resp.StatusCode != 200 {
    return fmt.Errorf("API key/endpoint validation failed (status %d)", resp.StatusCode)
}

Try / catch

_, err := provider.Stream(ctx, req)
if err != nil {
    msg := err.Error()
    switch {
    case strings.Contains(msg, "401"):
        // fix credentials
    case strings.Contains(msg, "429"):
        // backoff and retry respecting Retry-After
    case strings.Contains(msg, "400"):
        // fix model name / unsupported params
    default:
        // 5xx: retry with backoff
    }
    return err
}

Prevention

When it happens

Trigger: Any Stream() call where httpResp.StatusCode != http.StatusOK: 401 invalid API key, 429 rate limit / quota exceeded, 400 bad request (unknown model or parameter like a bad reasoning_effort), 500/503 OpenAI-side outage.

Common situations: Expired or revoked OPENAI_API_KEY; exceeded quota or hit rate limits; requesting a model the account/key can't access (e.g. o1 models needing verified org); passing Effort values the API rejects; regional outages.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/57768d82d8e6d844. Report an issue: GitHub.