{"record":{"id":"28f3260ecd50af2f","repo":"router-for-me/CLIProxyAPI","slug":"plugin-store-resolved-auth-expired","errorCode":null,"errorMessage":"plugin store resolved auth expired","messagePattern":"plugin store resolved auth expired","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginstore/auth.go","lineNumber":340,"sourceCode":"\t\treturn fmt.Errorf(\"insecure plugin store url requires matching allow-insecure auth rule\")\n\t}\n\treturn nil\n}\n\nfunc allowInsecurePluginStoreURL(auth []AuthConfig, requestURL string, kind string) bool {\n\titem, ok := matchingAuthConfig(auth, requestURL, kind)\n\treturn ok && item.AllowInsecure\n}\n\nfunc validateResolvedAuthExpiry(auth []ResolvedAuthConfig, expiresAt time.Time, now time.Time, requestURL string, kind string) error {\n\tif expiresAt.IsZero() {\n\t\treturn nil\n\t}\n\tif _, ok := matchingResolvedAuthConfig(auth, requestURL, kind); !ok {\n\t\treturn nil\n\t}\n\tif !now.Before(expiresAt) {\n\t\treturn fmt.Errorf(\"plugin store resolved auth expired\")\n\t}\n\treturn nil\n}\n\nfunc matchingAuthConfig(auth []AuthConfig, requestURL string, kind string) (AuthConfig, bool) {\n\trequestURL = strings.TrimSpace(requestURL)\n\tkind = strings.ToLower(strings.TrimSpace(kind))\n\tfor _, item := range NormalizeAuthConfigs(auth) {\n\t\tif !pluginStoreURLMatchesAuthRule(requestURL, item.Match) {\n\t\t\tcontinue\n\t\t}\n\t\tif !authAppliesTo(item, kind) {\n\t\t\tcontinue\n\t\t}\n\t\treturn item, true\n\t}\n\treturn AuthConfig{}, false\n}","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/auth.go#L322-L358","documentation":"validateResolvedAuthExpiry found that a cached/resolved plugin-store credential matching this URL and kind has passed its expiresAt (now is not before expiresAt). The store refuses to send a known-expired credential instead of provoking a guaranteed 401. Note the check only applies when a matching resolved auth config exists; unmatched URLs and zero expiry times are exempt.","triggerScenarios":"A resolved auth entry with an expiry (OAuth-derived store credential, short-lived token) matches the request URL and kind, and the current time is at or past expiresAt when a store fetch is attempted.","commonSituations":"Long-running server holding a cached store credential past its TTL; clock skew between the machine and the token issuer; token with a very short validity window; process resumed from suspend with stale auth state.","solutions":["Trigger re-resolution of the credential (re-run login / refresh the env-based token) so the store holds a fresh one","Check system clock sync (NTP) if the token should still be valid","Rotate to a longer-lived token for CI/automation contexts where refresh does not happen","Remove the expired resolved-auth entry so requests fall back to unauthenticated or another rule"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"func storeCredentialStillValid(resolved []ResolvedAuthConfig, requestURL, kind string, now time.Time) bool {\n    item, ok := matchingResolvedAuthConfig(resolved, requestURL, kind)\n    if !ok || item.ExpiresAt.IsZero() {\n        return true\n    }\n    return now.Before(item.ExpiresAt)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Refresh store credentials on a timer well before expiresAt (e.g. at 80% of TTL)","Keep system clocks NTP-synced so validity windows are evaluated correctly","For automation, prefer long-lived static tokens (env-based) over short-lived resolved credentials"],"tags":["auth","expiry","token","plugin-store"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}