googleapis/mcp-toolbox · error

failed to decode response: %w

Error message

failed to decode response: %w

What it means

The API returned HTTP 200 but the response body could not be decoded into FetchQueryStatsResponse. The library streams the body through json.Decoder and wraps any decoding failure.

Source

Thrown at internal/sources/databaseinsights/databaseinsights.go:390

	if err != nil {
		return nil, fmt.Errorf("failed to create http request: %w", err)
	}
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := s.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to execute request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		respBody, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("request failed with status %s: %s", resp.Status, string(respBody))
	}

	var fetchResp FetchQueryStatsResponse
	if err := json.NewDecoder(resp.Body).Decode(&fetchResp); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &fetchResp, nil
}

// FetchWaitEventStats executes the FetchWaitEventStats REST API method.
func (s *Source) FetchWaitEventStats(ctx context.Context, req *FetchWaitEventStatsRequest) (*FetchWaitEventStatsResponse, error) {
	url := fmt.Sprintf("%s/v1beta/%s/waitEventStats:fetch", s.getEndpointForParent(req.Parent), req.Parent)

	bodyBytes, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(bodyBytes))
	if err != nil {
		return nil, fmt.Errorf("failed to create http request: %w", err)
	}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Capture and inspect the raw response body to see what was actually returned
  2. Verify no proxy/gateway is rewriting responses (test from the same host with curl)
  3. Check for API version changes and update the FetchQueryStatsResponse struct fields/types
  4. Add defensive checks for empty bodies before decoding

Example fix

// before
var fetchResp FetchQueryStatsResponse
json.NewDecoder(resp.Body).Decode(&fetchResp) // fails on empty/HTML body
// after
body, _ := io.ReadAll(resp.Body)
if len(bytes.TrimSpace(body)) == 0 { return nil, fmt.Errorf("empty response body") }
json.Unmarshal(body, &fetchResp)
Defensive patterns

Strategy: type-guard

Validate before calling

// after a successful call, verify shape before use
func validFetchResp(r *FetchQueryStatsResponse) bool {
  return r != nil // extend with required-field checks
}

Type guard

func isDecodeError(err error) bool {
  var ute *json.UnmarshalTypeError
  var se *json.SyntaxError
  return errors.As(err, new(*json.SyntaxError)) || errors.As(err, &ute) || errors.As(err, &se)
}
// usage: if isDecodeError(err) { inspect raw body / check API version }

Try / catch

resp, err := src.FetchQueryStats(ctx, req)
if err != nil && strings.Contains(err.Error(), "failed to decode response") {
  // capture raw body via a debug HTTP client, check for HTML proxy pages or schema drift
  return fmt.Errorf("unexpected API response format: %w", err)
}

Prevention

When it happens

Trigger: json.NewDecoder(resp.Body).Decode fails: body is not valid JSON, is truncated, is HTML from a proxy/error page, or has a schema that mismatches FetchQueryStatsResponse (e.g. a string where a number is expected).

Common situations: An intermediary (proxy/gateway) returned an HTML error page with 200; API version skew introducing new field types; truncated response from network issues; empty body.

Understand the failure class

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/b66b329962ebe58d. Report an issue: GitHub.