{"record":{"id":"25583f4aed5eafc0","repo":"AlexxIT/go2rtc","slug":"nest-invalid-cache-key-format","errorCode":null,"errorMessage":"nest: invalid cache key format","messagePattern":"nest: invalid cache key format","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/nest/api.go","lineNumber":249,"sourceCode":"\t// Get the cached API with matching token to get credentials\n\tvar refreshKey string\n\tcacheMu.Lock()\n\tfor key, api := range cache {\n\t\tif api.Token == a.Token {\n\t\t\trefreshKey = key\n\t\t\tbreak\n\t\t}\n\t}\n\tcacheMu.Unlock()\n\n\tif refreshKey == \"\" {\n\t\treturn errors.New(\"nest: unable to find cached credentials\")\n\t}\n\n\t// Parse credentials from cache key\n\tparts := strings.Split(refreshKey, \":\")\n\tif len(parts) != 3 {\n\t\treturn errors.New(\"nest: invalid cache key format\")\n\t}\n\tclientID, clientSecret, refreshToken := parts[0], parts[1], parts[2]\n\n\t// Get new API instance which will refresh the token\n\tnewAPI, err := NewAPI(clientID, clientSecret, refreshToken)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Update current API with new token\n\ta.Token = newAPI.Token\n\ta.ExpiresAt = newAPI.ExpiresAt\n\treturn nil\n}\n\nfunc (a *API) ExtendStream() error {\n\tvar reqv struct {\n\t\tCommand string `json:\"command\"`","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/nest/api.go#L231-L267","documentation":"In refreshToken (pkg/nest/api.go:249), the cached refresh key is split on ':' and must yield exactly 3 parts: clientID, clientSecret, refreshToken. The library throws this error when the cached credential string does not have that clientID:clientSecret:refreshToken shape, because it cannot reconstruct the OAuth credentials needed to call NewAPI and refresh the token.","triggerScenarios":"ExchangeSDP calls refreshToken with a refreshKey whose strings.Split(key, \":\") returns != 3 parts — e.g. the key was cached with only 2 fields, with extra colons, or as an empty/malformed string.","commonSituations":"A credential cache entry written by an older library version with a different key layout; a client secret or refresh token that itself contains a ':' corrupting the field count; hand-edited or truncated cache storage (Redis/file); restoring cache values from a different integration.","solutions":["Clear the credential cache and re-authenticate so the key is written in the current clientID:clientSecret:refreshToken format.","Verify the cached value contains exactly two ':' separators and that clientSecret/refreshToken contain no raw ':' (URL-encode or re-store them if they do).","Check how the key was stored: ensure the code path that caches credentials uses the same 3-field format that refreshToken expects.","Upgrade/downgrade the library so the writer and reader of the cache key agree on the format."],"exampleFix":"// before: storing a 2-field key\nclient.Set(\"nest creds\", clientID + \":\" + refreshToken)\n// after: store all three fields, colon-free\nif strings.ContainsAny(clientSecret+refreshToken, \":\") { /* re-encode or error */ }\nclient.Set(\"nest creds\", clientID + \":\" + clientSecret + \":\" + refreshToken)","handlingStrategy":"validation","validationCode":"func validNestCacheKey(key string) bool {\n    parts := strings.Split(key, \":\")\n    return len(parts) == 3 && parts[0] != \"\" && parts[1] != \"\" && parts[2] != \"\" &&\n        !strings.ContainsAny(parts[1]+parts[2], \":\")\n}\nif !validNestCacheKey(refreshKey) { return fmt.Errorf(\"malformed nest cache key\") }","typeGuard":"func isNestCacheKey(v any) (clientID, clientSecret, refreshToken string, ok bool) {\n    s, ok := v.(string)\n    if !ok { return \"\", \"\", \"\", false }\n    parts := strings.Split(s, \":\")\n    if len(parts) != 3 { return \"\", \"\", \"\", false }\n    return parts[0], parts[1], parts[2], true\n}","tryCatchPattern":"creds, err := loadCachedCreds(userID)\nif err != nil || strings.Count(creds, \":\") != 2 {\n    // re-authenticate instead of calling ExchangeSDP\n    return reauthenticate(ctx, userID)\n}","preventionTips":["Always store credentials as exactly clientID:clientSecret:refreshToken via one shared helper.","Reject or re-encode secrets containing ':' before caching.","Version the cache key (e.g. \"v2:...\") so format changes invalidate old entries cleanly.","Test the parser round-trip (write then read) in CI."],"tags":["authentication","oauth","cache","format"],"backgroundTag":"invalid-argument-format","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}