{"record":{"id":"dc5650ce5e53f53e","repo":"chenhg5/cc-connect","slug":"googlechat-marshal-body-w","errorCode":null,"errorMessage":"googlechat: marshal body: %w","messagePattern":"googlechat: marshal body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"platform/googlechat/googlechat.go","lineNumber":394,"sourceCode":"\treturn u\n}\n\n// applyThread adds the thread field to body when rc has a thread.\nfunc applyThread(body map[string]any, rc replyContext) {\n\tif rc.thread != \"\" {\n\t\tbody[\"thread\"] = map[string]any{\"name\": rc.thread}\n\t}\n}\n\n// buildSendRequest builds the Chat REST API URL and JSON body to post content\n// into rc's space. When a thread is known the reply is threaded (falling back\n// to a new thread if that thread no longer accepts replies).\nfunc buildSendRequest(rc replyContext, content string) (string, []byte, error) {\n\tbody := map[string]any{\"text\": content}\n\tapplyThread(body, rc)\n\tb, err := json.Marshal(body)\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"googlechat: marshal body: %w\", err)\n\t}\n\treturn messageURL(rc), b, nil\n}\n\n// doRequest executes req using botClient and returns the response on success.\n// On non-2xx it reads the error body, closes it, and returns an error.\n// The caller is responsible for draining and closing resp.Body on success.\nfunc (p *Platform) doRequest(req *http.Request) (*http.Response, error) {\n\tresp, err := p.botClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif resp.StatusCode >= 300 {\n\t\treturn nil, httpErrorBody(resp, fmt.Sprintf(\"googlechat: %s %s\", req.Method, req.URL.Path))\n\t}\n\treturn resp, nil\n}\n","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/googlechat/googlechat.go#L376-L412","documentation":"buildSendRequest serializes the Chat message payload ({\"text\": ...} plus thread fields) to JSON before posting. json.Marshal of this fixed-shape map should never fail in practice (no channels, funcs, or unsupported types are placed in it), so this error indicates an internal invariant violation rather than a caller mistake. Callers (post, tests, SendPreviewStart) receive \"\" URL and nil body alongside the wrapped error.","triggerScenarios":"Effectively only reachable if map values become non-marshalable (e.g. a future code change inserts a func or channel into the body map); not triggerable from configuration or normal API usage today.","commonSituations":"Encountered only during development/refactoring of buildSendRequest or applyThread when unusual values are added to the payload map.","solutions":["If it appears, inspect any recent changes to buildSendRequest/applyThread that added new body fields","Ensure all values placed in the body map are JSON-serializable types (string, number, bool, nested maps/slices)","Report as a bug with the wrapped inner error message if hit on a released binary"],"exampleFix":"// before\nbody[\"cards\"] = someFunc  // not JSON-marshalable\n\n// after\nbody[\"cards\"] = renderedCardMap // plain map[string]any","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isReplyContext(v any) bool { _, ok := v.(replyContext); return ok }","tryCatchPattern":"url, body, err := buildSendRequest(rc, content)\nif err != nil {\n    return fmt.Errorf(\"googlechat: build payload: %w\", err) // internal bug; report\n}","preventionTips":["Only put JSON-serializable values (string, number, bool, map, slice) into the send body map","Add a unit test asserting buildSendRequest succeeds for all reply-context shapes","Treat occurrences as an internal bug to report, not a config issue"],"tags":["googlechat","json","internal"],"backgroundTag":"json-marshal-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}