{"record":{"id":"eafa7a0062dac96c","repo":"chenhg5/cc-connect","slug":"s-status-d-s","errorCode":null,"errorMessage":"%s: status %d: %s","messagePattern":"(.+?): status (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/googlechat/googlechat.go","lineNumber":358,"sourceCode":"\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)\n\t}\n\treturn fmt.Errorf(\"%s: status %d: %s\", prefix, resp.StatusCode, strings.TrimSpace(string(b)))\n}\n\n// coalesce returns s if non-empty, otherwise def.\nfunc coalesce(s, def string) string {\n\tif s != \"\" {\n\t\treturn s\n\t}\n\treturn def\n}\n\n// messageURL returns the Chat messages endpoint for rc's space, appending the\n// messageReplyOption query when a thread is known.\nfunc messageURL(rc replyContext) string {\n\tu := chatAPIBase + rc.space + \"/messages\"\n\tif rc.thread != \"\" {\n\t\tu += \"?messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD\"\n\t}\n\treturn u","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/googlechat/googlechat.go#L340-L376","documentation":"This is the standard error returned for any non-2xx response from the Google Chat REST API. httpErrorBody reads up to 2048 bytes of the API's error body, trims it, and combines it with the HTTP status code and the caller's prefix (e.g. \"googlechat: send message\"), giving the developer both the status and Google's error explanation.","triggerScenarios":"Any Chat API call via doRequest that returns 4xx/5xx: posting to a deleted or inaccessible space (404), bot not added to the space (403), invalid thread key (400), quota exceeded (429), or Google-side 5xx.","commonSituations":"Bot removed from the space; wrong space/thread resource name stored in a session; service account lacking chat.messages authorization scope; exceeding API rate limits; expired OAuth token surfacing as 401.","solutions":["Read the status and embedded Google error body in the message — it names the exact cause (e.g. PERMISSION_DENIED, NOT_FOUND)","For 403/404, verify the bot is still a member of the target space and the space name (spaces/AAAA) is valid; re-create the session if the space changed","For 401, refresh the OAuth token / re-authorize scopes (chat.messages); for 429, add backoff/retry around sends"],"exampleFix":"// before\nPOST https://chat.googleapis.com/v1/spaces/OLD/messages -> 404 NOT_FOUND\n// after\nFetch the correct current space via spaces.list (or re-add the bot) and use the new space name in the reply context","handlingStrategy":"retry","validationCode":"// before sending, confirm the bot can access the space\nresp, _ := client.Get(\"https://chat.googleapis.com/v1/\" + spaceName + \"?access_token=\" + tok)\n// treat non-200 as \"space unreachable\" and skip/recreate session","typeGuard":null,"tryCatchPattern":"err := p.Send(ctx, rc, msg)\nif err != nil {\n    var ge *googleapi.Error\n    if errors.As(err, &ge) {\n        switch ge.Code {\n        case 429: time.Sleep(backoff); retry()\n        case 401: refreshToken(); retry()\n        case 403, 404: invalidateSession(rc)\n        }\n    }\n}","preventionTips":["Handle 429 with exponential backoff; 401 with token refresh; 403/404 by invalidating the stored session","Periodically verify the bot's membership in target spaces","Keep chat.message and chat.bot OAuth scopes current"],"tags":["googlechat","http","api","rest"],"backgroundTag":"http-error-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}