{"record":{"id":"86b6f1c5e52a6af7","repo":"googleapis/mcp-toolbox","slug":"error-decoding-stream-message-w","errorCode":null,"errorMessage":"error decoding stream message: %w","messagePattern":"error decoding stream message: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/tools/looker/lookerconversationalanalytics/lookerconversationalanalytics.go","lineNumber":467,"sourceCode":"\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 {\n\t\t\t\tnewMessage = handleDataResponse(ctx, msg.SystemMessage.Data)\n\t\t\t} else if msg.SystemMessage.Analysis != nil {\n\t\t\t\tnewMessage = handleAnalysisResponse(ctx, msg.SystemMessage.Analysis)\n\t\t\t} else if msg.SystemMessage.Error != nil {\n\t\t\t\tnewMessage = handleError(ctx, msg.SystemMessage.Error)\n\t\t\t}\n\t\t\tmessages = appendMessage(messages, newMessage)\n\t\t}\n\t}","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/looker/lookerconversationalanalytics/lookerconversationalanalytics.go#L449-L485","documentation":"While iterating the JSON array with decoder.More(), each element is decoded into a StreamMessage struct. This error wraps any decoding failure of an individual stream element that is not io.EOF, meaning an element in the array does not match the expected StreamMessage shape (or the stream ended mid-element).","triggerScenarios":"The API emits a stream element with unexpected/extra structure that fails strict decoding into StreamMessage, or the connection is cut mid-element so a partial JSON object is decoded.","commonSituations":"API version drift introducing new or differently-shaped stream events; corrupted stream due to network interruption; intermediary mangling chunked transfer encoding; the server emitting an error object inside the stream.","solutions":["Check the wrapped error for the exact JSON field/type mismatch and update the StreamMessage struct (or the library) to match the current API response shape.","Pin/verify the API version being used and check for Conversational Analytics API changelog changes.","Retry the call to rule out a truncated stream from a transient network cut.","Capture the full raw stream with curl for a successful run and compare against the failing payload."],"exampleFix":"// before: struct missing a newly added event field\ntype StreamMessage struct {\n    SystemMessage *SystemMessage `json:\"systemMessage\"`\n}\n// after: tolerate unknown/added fields without breaking decode\ntype StreamMessage struct {\n    SystemMessage *SystemMessage `json:\"systemMessage\"`\n    Extra         map[string]any `json:\"-\"` // inspect payloads during debug\n}","handlingStrategy":"try-catch","validationCode":"// Go: sanity-check one stream element against the expected shape before bulk decoding\nfunc looksLikeStreamElement(el map[string]any) bool {\n    _, ok := el[\"systemMessage\"]\n    return ok\n}","typeGuard":"// Go: narrow via a tolerant probe decode\ntype StreamMessage struct {\n    SystemMessage *SystemMessage `json:\"systemMessage\"`\n}\nfunc asStreamMessage(raw json.RawMessage) (*StreamMessage, error) {\n    var msg StreamMessage\n    dec := json.NewDecoder(bytes.NewReader(raw))\n    dec.DisallowUnknownFields = false // tolerate new API fields\n    if err := dec.Decode(&msg); err != nil {\n        return nil, fmt.Errorf(\"unexpected stream element: %w\", err)\n    }\n    return &msg, nil\n}","tryCatchPattern":"result, err := invokeTool(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"error decoding stream message\") {\n    log.Printf(\"stream element shape mismatch, likely API drift: %v\", err)\n    // fall back to raw passthrough or retry against pinned API version\n    return rawFallback(ctx, req)\n}","preventionTips":["Pin the Conversational Analytics API version and review its changelog.","Keep StreamMessage structs permissive (ignore unknown fields).","Log failing raw elements to identify the mismatching field quickly.","Handle mid-stream disconnects with resumable/retry logic."],"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"}