{"record":{"id":"5facd111072b5a48","repo":"googleapis/mcp-toolbox","slug":"failed-to-decode-response-body-w","errorCode":null,"errorMessage":"failed to decode response body: %w","messagePattern":"failed to decode response body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/elasticsearch/elasticsearch.go","lineNumber":207,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer res.Body.Close()\n\n\tif res.IsError() {\n\t\t// Try to extract error message from response\n\t\tvar esErr json.RawMessage\n\t\terr = util.DecodeJSON(res.Body, &esErr)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"elasticsearch error: status %s\", res.Status())\n\t\t}\n\t\treturn esErr, nil\n\t}\n\n\tvar result EsqlResult\n\terr = util.DecodeJSON(res.Body, &result)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode response body: %w\", err)\n\t}\n\n\toutput := EsqlToMap(result)\n\n\treturn output, nil\n}\n\n// EsqlToMap converts the esqlResult to a slice of maps.\nfunc EsqlToMap(result EsqlResult) []map[string]any {\n\toutput := make([]map[string]any, 0, len(result.Values))\n\tfor _, value := range result.Values {\n\t\trow := make(map[string]any)\n\t\tif value == nil {\n\t\t\toutput = append(output, row)\n\t\t\tcontinue\n\t\t}\n\t\tfor i, col := range result.Columns {\n\t\t\tif i < len(value) {","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/elasticsearch/elasticsearch.go#L189-L225","documentation":"Thrown in RunSQL when the ES|QL query succeeds (no HTTP error) but the response body cannot be decoded into the EsqlResult struct {columns, values}. Note FilterPath restricts the response to columns/values, so the wrapper means the JSON shape differed from expectations or was corrupt.","triggerScenarios":"The `format` parameter requests a non-JSON format (e.g. csv, tsv) so the body is not JSON at all; a proxy rewrites the body; an intermediate error page appears with a 200; or ES returns a JSON shape missing `columns`/`values`.","commonSituations":"Users setting format to csv/text in the tool config expecting delimited output — the client still tries to JSON-decode it; LLM passing format values unsupported for JSON decoding; old Elasticsearch versions (<8.11) lacking the _query endpoint and returning something unexpected.","solutions":["Ensure `format` is empty or \"json\" so the response body is JSON","Check Elasticsearch version supports ES|QL (8.11+ or serverless)","Inspect the raw response with curl (`POST /_query?format=json`) to see the actual body","Remove any proxy that alters response bodies"],"exampleFix":"// before (tools.yaml tool parameter)\nformat: \"csv\"\n// after\nformat: \"json\"","handlingStrategy":"validation","validationCode":"// only JSON output can be decoded by the toolbox\nif format != \"\" && format != \"json\" {\n    return fmt.Errorf(\"format %q not supported; use empty or \\\"json\\\"\", format)\n}","typeGuard":null,"tryCatchPattern":"// Go\nout, err := src.RunSQL(ctx, format, query, params)\nif err != nil && strings.Contains(err.Error(), \"failed to decode response body\") {\n    // check format param and ES version; retry with format=\"json\"\n}","preventionTips":["Leave `format` unset or use \"json\" for the ES|QL tool","Require Elasticsearch 8.11+ (or serverless) for ES|QL support","Probe the endpoint with curl to confirm the body contains columns/values"],"tags":["elasticsearch","response-parsing","json","esql"],"backgroundTag":"response-decode-failed","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"}