{"record":{"id":"169d6a75051bd1fb","repo":"chenhg5/cc-connect","slug":"googlechat-invalid-session-key-q","errorCode":null,"errorMessage":"googlechat: invalid session key %q","messagePattern":"googlechat: invalid session key %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/googlechat/googlechat.go","lineNumber":338,"sourceCode":"\t\tif thread != \"\" {\n\t\t\treturn sessionKeyPrefix + space + threadSep + thread\n\t\t}\n\t\treturn sessionKeyPrefix + space\n\tcase \"user\":\n\t\treturn sessionKeyPrefix + space + \":\" + user\n\tdefault:\n\t\treturn sessionKeyPrefix + space\n\t}\n}\n\n// ReconstructReplyCtx rebuilds a reply context from a session key so proactive\n// sends (cron, send-to-session, restart notices) can reach the right space and\n// thread. Implements core.ReplyContextReconstructor.\nfunc (p *Platform) ReconstructReplyCtx(sessionKey string) (any, error) {\n\t// googlechat:<space>  |  googlechat:<space>:t:<thread>  |  googlechat:<space>:<user>\n\trest, ok := strings.CutPrefix(sessionKey, sessionKeyPrefix)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"googlechat: invalid session key %q\", sessionKey)\n\t}\n\tif idx := strings.Index(rest, threadSep); idx != -1 {\n\t\treturn replyContext{space: rest[:idx], thread: rest[idx+len(threadSep):]}, nil\n\t}\n\t// User-scoped keys append \":<user>\" where user is \"users/<id>\"; strip a\n\t// trailing \"users/...\" segment to recover the bare space.\n\tif idx := strings.LastIndex(rest, \":users/\"); idx != -1 {\n\t\treturn replyContext{space: rest[:idx]}, nil\n\t}\n\treturn replyContext{space: rest}, nil\n}\n\n// httpErrorBody reads up to 2048 bytes from resp.Body, closes it, and returns\n// an error combining prefix, status code, and the response snippet.\nfunc httpErrorBody(resp *http.Response, prefix string) error {\n\tb, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))\n\tif err := resp.Body.Close(); err != nil {\n\t\treturn fmt.Errorf(\"%s: status %d: %s (close body: %v)\", prefix, resp.StatusCode, strings.TrimSpace(string(b)), err)","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/googlechat/googlechat.go#L320-L356","documentation":"ReconstructReplyCtx turns a persisted session key back into a replyContext so outbound sends (cron jobs, send-to-session, restart notices) can target the right space/thread. It throws this error when sessionKey does not start with the expected \"googlechat:\" prefix, meaning the key is not a googlechat session key and cannot be reconstructed.","triggerScenarios":"Passing a session key produced by another platform (e.g. \"telegram:12345\"), a bare space name, an empty string, or a manually built key missing the \"googlechat:\" prefix to ReconstructReplyCtx.","commonSituations":"Cron or scheduled-send configs that hardcode a session key copied from the wrong platform; database rows created before googlechat support where the prefix scheme differs; code that constructs keys by hand instead of using the platform's session-key format.","solutions":["Prefix the key with \"googlechat:\" (e.g. googlechat:spaces/AAA:t/THREAD or googlechat:spaces/AAA)","Check where the session key is persisted — ensure cron/send-to-session jobs target googlechat sessions only","Log/inspect the stored key to confirm it was written by the googlechat platform and not truncated"],"exampleFix":"// before\nrc, err := p.ReconstructReplyCtx(\"spaces/AAAA\")\n\n// after\nrc, err := p.ReconstructReplyCtx(\"googlechat:spaces/AAAA\")","handlingStrategy":"validation","validationCode":"func validGoogleChatKey(k string) bool { return strings.HasPrefix(k, \"googlechat:\") }\nif !validGoogleChatKey(sessionKey) { return fmt.Errorf(\"not a googlechat session key: %q\", sessionKey) }","typeGuard":null,"tryCatchPattern":"rc, err := p.ReconstructReplyCtx(sessionKey)\nif err != nil {\n    slog.Warn(\"cannot reconstruct googlechat reply ctx; skipping send\", \"key\", sessionKey, \"err\", err)\n    return err\n}","preventionTips":["Always persist session keys with their platform prefix; never hand-build them","When scheduling sends, resolve the key through the same platform that created the session","Add a startup check that scans stored session keys for the correct prefix"],"tags":["googlechat","session","identifier"],"backgroundTag":"invalid-identifier-format","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"}