micro/go-micro · error
stream API error (%s): %s
Error message
stream API error (%s): %s
What it means
The Ollama Cloud OpenAI-compatible endpoint returned a non-200 HTTP status for a streaming chat request. The error embeds the HTTP status string (e.g. '401 Unauthorized') and the raw response body, which usually holds the server's JSON error explaining why.
Source
Thrown at ai/ollama/ollama.go:354
apiURL := strings.TrimRight(p.opts.BaseURL, "/") + p.streamPath()
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")
if p.opts.APIKey != "" {
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 &sseStream{body: httpResp.Body, scanner: bufio.NewScanner(httpResp.Body)}, nil
}
// buildOpenAIMessages converts an ai.Request into the OpenAI chat message format.
func buildOpenAIMessages(req *ai.Request) []map[string]any {
messages := []map[string]any{}
if req.SystemPrompt != "" {
messages = append(messages, map[string]any{"role": "system", "content": req.SystemPrompt})
}
for _, m := range req.Messages {
messages = append(messages, map[string]any{"role": m.Role, "content": m.Content})
}
if req.Prompt != "" {
messages = append(messages, map[string]any{"role": "user", "content": req.Prompt})
}
return messagesView on GitHub (pinned to 24529f1404)
Solutions
- Read the body in the error message — it contains the server's reason.
- 401/403: set a valid API key via WithAPIKey or the OLLAMA_API_KEY env var.
- 404: fix BaseURL so it ends at the host root or /v1, not a duplicated path.
- 400: verify the model name exists on the cloud endpoint.
- 429/5xx: back off and retry; the server is rate-limiting or unhealthy.
Example fix
// before
provider := ai.NewProvider("ollama", ai.WithBaseURL("https://ollama.com/v1")) // no API key -> 401
// after
provider := ai.NewProvider("ollama", ai.WithBaseURL("https://ollama.com/v1"), ai.WithAPIKey(os.Getenv("OLLAMA_API_KEY"))) Defensive patterns
Strategy: try-catch
Validate before calling
if os.Getenv("OLLAMA_API_KEY") == "" {
return errors.New("OLLAMA_API_KEY is required for ollama.com cloud endpoints")
} Try / catch
stream, err := provider.Stream(ctx, req)
if err != nil {
var apiErr *ApiError // or match on status substring
msg := err.Error()
switch {
case strings.Contains(msg, "401"), strings.Contains(msg, "403"):
return errors.New("authentication failed: check OLLAMA_API_KEY")
case strings.Contains(msg, "429"):
// back off and retry
default:
return err
}
} Prevention
- Validate the API key with a cheap models-list call before streaming.
- Never hardcode keys; load from env/secret manager and check presence at boot.
- Pin a correct BaseURL constant instead of assembling it ad hoc.
- Monitor for 429s and implement client-side rate limiting.
When it happens
Trigger: Stream() in cloud mode receives any status != 200: 401/403 (bad/missing API key), 404 (wrong path/BaseURL), 400 (malformed model or request), 429/5xx from ollama.com.
Common situations: Missing or expired OLLAMA_API_KEY, using an API key without cloud access, BaseURL pointing to /v1 incorrectly, requesting a model name that doesn't exist on ollama.com, rate limiting.
Related errors
- API error (%s): %s
- gemini stream error (%s): %s
- stream API request failed: %w
- stream API error (%s): %s
- agent: StreamAsk unsupported by implementation
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/d2bd9f090a4df1b5.
Report an issue: GitHub.