micro/go-micro · error

failed to parse response: %w

Error message

failed to parse response: %w

What it means

callAPI wraps the error from json.Unmarshal when the HTTP 200 body cannot be decoded into the expected chat-completion response struct. It means the API returned a 200 whose JSON shape does not match the expected {choices:[{message:{...}}]} schema. The library throws it to surface the decode failure with the underlying *json.UnmarshalTypeError or *json.SyntaxError attached.

Source

Thrown at ai/atlascloud/atlascloud.go:466

		retryAfter := time.Duration(0)
		var retryErr interface{ RetryAfter() time.Duration }
		if errors.As(ai.NewHTTPError(httpResp, respBody), &retryErr) {
			retryAfter = retryErr.RetryAfter()
		}
		return nil, nil, &atlascloudAPIError{Status: httpResp.Status, Code: httpResp.StatusCode, Retry: retryAfter, Phase: phase, Summary: atlascloudRequestSummary(req), Body: string(respBody)}
	}

	var chatResp struct {
		Choices []struct {
			Message struct {
				Content   string          `json:"content"`
				ToolCalls []atlasToolCall `json:"tool_calls"`
			} `json:"message"`
		} `json:"choices"`
	}

	if err := json.Unmarshal(respBody, &chatResp); err != nil {
		return nil, nil, fmt.Errorf("failed to parse response: %w", err)
	}

	if len(chatResp.Choices) == 0 {
		return nil, nil, fmt.Errorf("no response from API")
	}

	choice := chatResp.Choices[0]
	response := &ai.Response{
		Reply: choice.Message.Content,
	}

	for _, tc := range choice.Message.ToolCalls {
		var input map[string]any
		if err := json.Unmarshal([]byte(tc.Function.Arguments), &input); err != nil {
			input = map[string]any{}
		}
		response.ToolCalls = append(response.ToolCalls, ai.ToolCall{
			ID:    tc.ID,

View on GitHub (pinned to 24529f1404)

Solutions

  1. Log string(respBody) on this error to see what the API actually returned and compare against the expected chat response schema.
  2. Confirm the API version/model endpoint matches what this provider version expects; pin or update the provider code if AtlasCloud changed its schema.
  3. Check for intermediaries returning HTML (proxy auth pages) by inspecting Content-Type of the response.
  4. Capture the error from io.ReadAll instead of discarding it so truncated bodies are detected before unmarshal.

Example fix

if err := json.Unmarshal(respBody, &chatResp); err != nil {
    return nil, nil, fmt.Errorf("failed to parse response: %w (body: %.200s)", err, string(respBody))
}
Defensive patterns

Strategy: try-catch

Type guard

func isValidChatResponse(b []byte) bool {
    var probe struct {
        Choices []json.RawMessage `json:"choices"`
    }
    return json.Unmarshal(b, &probe) == nil
}

Try / catch

resp, err := provider.Generate(ctx, req)
if err != nil {
    if strings.Contains(err.Error(), "failed to parse response") {
        var uerr *json.UnmarshalTypeError
        if errors.As(err, &uerr) {
            log.Printf("schema mismatch at %s: %v", uerr.Field, uerr)
        }
    }
    return err
}

Prevention

When it happens

Trigger: json.Unmarshal(respBody, &chatResp) returns an error: the body is empty, truncated, HTML (e.g. a proxy/login page), or field types diverge from the struct (e.g. content returned as an array or null where a string is expected).

Common situations: Provider changed its response schema after an API version update; an intermediary (gateway, WAF, captive portal) returned HTML with status 200; response body was read incompletely because io.ReadAll's error was ignored; model returned tool_calls in an unexpected format.

Understand the failure class

Related errors


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