{"record":{"id":"e0afe86a92c3a7c7","repo":"googleapis/mcp-toolbox","slug":"error-reading-start-of-json-array-w-e0afe8","errorCode":null,"errorMessage":"error reading start of json array: %w","messagePattern":"error reading start of json array: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/tools/looker/lookerconversationalanalytics/lookerconversationalanalytics.go","lineNumber":458,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to send request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn nil, fmt.Errorf(\"API returned non-200 status: %d %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar messages []map[string]any\n\tdecoder := json.NewDecoder(resp.Body)\n\n\t// The response is a JSON array, so we read the opening bracket.\n\tif _, err := decoder.Token(); err != nil {\n\t\tif err == io.EOF {\n\t\t\treturn nil, nil // Empty response is valid\n\t\t}\n\t\treturn nil, fmt.Errorf(\"error reading start of json array: %w\", err)\n\t}\n\n\tfor decoder.More() {\n\t\tvar msg StreamMessage\n\t\tif err := decoder.Decode(&msg); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"error decoding stream message: %w\", err)\n\t\t}\n\n\t\tvar newMessage map[string]any\n\t\tif msg.SystemMessage != nil {\n\t\t\tif msg.SystemMessage.Text != nil {\n\t\t\t\tnewMessage = handleTextResponse(ctx, msg.SystemMessage.Text)\n\t\t\t} else if msg.SystemMessage.Schema != nil {\n\t\t\t\tnewMessage = handleSchemaResponse(ctx, msg.SystemMessage.Schema)\n\t\t\t} else if msg.SystemMessage.Data != nil {","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/looker/lookerconversationalanalytics/lookerconversationalanalytics.go#L440-L476","documentation":"The tool streams the API response as a JSON array and reads its opening '[' token with json.Decoder.Token(). This error wraps any non-EOF failure while parsing that first token, meaning the response body is not the expected JSON array. Empty bodies (io.EOF) are explicitly treated as valid and return nil, so this always indicates malformed or unexpected content.","triggerScenarios":"The endpoint returned an HTML error page, a truncated response, a proxy-captured captive-portal page, or otherwise corrupted/non-JSON body while the status was still 200.","commonSituations":"A reverse proxy or API gateway intercepting the stream and returning HTML; response body truncated by a proxy/timeout mid-stream; wrong endpoint URL returning an unexpected payload; gzip/content-encoding mishandling by an intermediary.","solutions":["Log the raw response body (or replay the request with curl) to see what was actually returned instead of the JSON array.","Bypass intermediaries (proxies, gateways, WAFs) that may rewrite or truncate the streaming response.","Confirm the endpoint URL and API version are correct for Conversational Analytics.","Retry, and if it persists, verify the response Content-Type is application/json and encoding is handled correctly."],"exampleFix":"// before: assuming 200 means a JSON array\nif resp.StatusCode != http.StatusOK { ... }\nvar messages []map[string]any\n// after: defensively check content type before decoding\nif ct := resp.Header.Get(\"Content-Type\"); !strings.Contains(ct, \"json\") {\n    return nil, fmt.Errorf(\"unexpected content-type: %s\", ct)\n}\nvar messages []map[string]any","handlingStrategy":"fallback","validationCode":"// Go: validate content type before parsing the stream\nfunc isJSONResponse(resp *http.Response) bool {\n    return strings.Contains(resp.Header.Get(\"Content-Type\"), \"application/json\")\n}","typeGuard":null,"tryCatchPattern":"result, err := invokeTool(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"error reading start of json array\") {\n    log.Printf(\"non-JSON stream received, retrying once: %v\", err)\n    result, err = invokeTool(ctx, req) // intermediaries often intercept once\n    if err != nil { return nil, fmt.Errorf(\"stream unreadable: %w\", err) }\n}","preventionTips":["Bypass or correctly configure proxies/gateways for streaming responses.","Verify response Content-Type is application/json when debugging.","Check that no middleware compresses/rewrites chunked responses unexpectedly.","Capture raw bodies with curl when the payload shape is in doubt."],"tags":["json","streaming","looker","parsing"],"backgroundTag":"json-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"}