{"record":{"id":"8ee24be00217f490","repo":"googleapis/mcp-toolbox","slug":"request-failed-s-body-s","errorCode":null,"errorMessage":"request failed: %s, body: %s","messagePattern":"request failed: (.+?), body: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/cloudmonitoring/cloud_monitoring.go","lineNumber":167,"sourceCode":"\tq := req.URL.Query()\n\tq.Add(\"query\", query)\n\treq.URL.RawQuery = q.Encode()\n\n\treq.Header.Set(\"User-Agent\", s.UserAgent())\n\n\tresp, err := s.Client().Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read response body: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"request failed: %s, body: %s\", resp.Status, string(body))\n\t}\n\n\tif len(body) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tvar result map[string]any\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal json: %w, body: %s\", err, string(body))\n\t}\n\n\treturn result, nil\n}\n","sourceCodeStart":149,"sourceCodeEnd":181,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/cloudmonitoring/cloud_monitoring.go#L149-L181","documentation":"RunQuery treats only HTTP 200 as success. Any other status code — 400 bad request, 401/403 auth problems, 404, 429 rate limits, 5xx server errors — produces this error, with the status line and the response body (which usually contains the Google API JSON error details) appended.","triggerScenarios":"Executing a monitoring query whose HTTP response has a non-200 status: malformed query (MQL/TimeSeries filter), expired or insufficient-permission credentials, quota exhaustion, or GCP-side errors.","commonSituations":"Invalid Monitoring Query Language syntax; service account lacking monitoring.timeSeries.read permissions; hitting API quotas; project misconfiguration.","solutions":["Read the body in the error message — it contains the Google API error JSON with the precise cause","For 401/403, refresh credentials and grant the monitoring viewer role","For 429, implement backoff and reduce request rate","For 400, validate the MQL/filter syntax and time range parameters"],"exampleFix":"// before\nq := \"fetch invalid::metric\" // bad MQL\n// after\nq := \"fetch gce_instance::compute.googleapis.com/instance/cpu/utilization\"","handlingStrategy":"try-catch","validationCode":"// validate MQL/filter and time range before calling RunQuery\nif query == \"\" || startTime.After(endTime) {\n    return fmt.Errorf(\"invalid monitoring query parameters\")\n}","typeGuard":null,"tryCatchPattern":"result, err := src.RunQuery(ctx, query)\nif err != nil {\n    var apiErr struct {\n        Error struct {\n            Code    int    `json:\"code\"`\n            Status  string `json:\"status\"`\n            Message string `json:\"message\"`\n        } `json:\"error\"`\n    }\n    if i := strings.Index(err.Error(), \"body: {\"); i >= 0 {\n        if json.Unmarshal([]byte(err.Error()[i+6:]), &apiErr) == nil {\n            switch apiErr.Error.Code {\n            case 429: scheduleRetry()\n            case 403: log.Printf(\"permission denied: %s\", apiErr.Error.Message)\n            }\n        }\n    }\n    return err\n}","preventionTips":["Parse the embedded body JSON to distinguish auth vs quota vs syntax errors","Grant monitoring.viewer to the service account","Implement exponential backoff for 429/5xx","Validate MQL syntax against documented examples before deployment"],"tags":["http","cloud-monitoring","api-error"],"backgroundTag":"http-non-200-response","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}