{"record":{"id":"94e06a111b5aef82","repo":"grpc/grpc-go","slug":"expired-token","errorCode":null,"errorMessage":"expired token","messagePattern":"expired token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/jwt/file_reader.go","lineNumber":114,"sourceCode":"\tpayloadBytes, err := base64.RawURLEncoding.DecodeString(claimsRaw)\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"decode error: %v\", err)\n\t}\n\n\tvar claims jwtClaims\n\tif err := json.Unmarshal(payloadBytes, &claims); err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"unmarshal error: %v\", err)\n\t}\n\n\tif claims.Exp == 0 {\n\t\treturn time.Time{}, fmt.Errorf(\"no expiration claims\")\n\t}\n\n\texpTime := time.Unix(claims.Exp, 0)\n\n\t// Check if token is already expired.\n\tif expTime.Before(time.Now()) {\n\t\treturn time.Time{}, fmt.Errorf(\"expired token\")\n\t}\n\n\treturn expTime, nil\n}\n","sourceCodeStart":96,"sourceCodeEnd":119,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/jwt/file_reader.go#L96-L119","documentation":"After extracting exp, if time.Unix(claims.Exp,0).Before(time.Now()) extractExpiration returns 'expired token' (file_reader.go:110-114). The token was structurally valid but its exp claim is in the past, so the credential refuses to use it.","triggerScenarios":"The token file holds a JWT whose exp already passed. Common with long-running processes that read a token once and never refresh, a token-rotating sidecar that stopped updating the file, or a static token captured for debugging.","commonSituations":"Projected token volume rotation disabled or delayed, a CI job using a hardcoded token committed long ago, clock skew where the local clock is ahead, or a short-lived token read after its lifetime elapsed.","solutions":["Refresh the token file with a current JWT (re-mint or let the rotating sidecar/kubelet repopulate it).","Ensure the file-reader is pointed at a path that is continuously refreshed (e.g. projected service-account token volume with rotation).","Sync clocks (NTP/chrony) to rule out skew before treating the token as genuinely expired.","For long-lived processes, re-read the file on each use rather than caching the first read."],"exampleFix":"// before: one-shot read of a token that later expired\ntok, exp, err := r.readToken() // cached forever\n\n// after: re-read before each use so rotations are picked up\ntok, exp, err := r.readToken()\nif err != nil { return err } // forces a fresh file read next call","handlingStrategy":"fallback","validationCode":"// Pre-check expiry against a clock so the credential is not handed an already-dead token.\nexp, err := extractExp(tok)\nif err != nil { return err }\nif !exp.After(time.Now()) {\n    return errors.New(\"token already expired\")\n}","typeGuard":null,"tryCatchPattern":"tok, _, err := r.readToken()\nif err != nil && strings.Contains(err.Error(), \"expired token\") {\n    // refresh the token file (sidecar/kubelet rotation) and retry.\n    return err\n}","preventionTips":["Point the reader at a continuously-rotated token file (projected SA token volume).","Re-read the file on each use rather than caching the first read.","Sync clocks with NTP to rule out skew.","Avoid committing long-lived static tokens to repos or images."],"tags":["grpc","jwt","expiration","credentials","configuration"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}