{"record":{"id":"5b499e470a11c0dd","repo":"grpc/grpc-go","slug":"token-file-q-v-w","errorCode":null,"errorMessage":"token file %q: %v: %w","messagePattern":"token file %q: (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/jwt/file_reader.go","lineNumber":62,"sourceCode":"\ttokenFilePath string\n}\n\n// readToken reads and parses a JWT token from the configured file.\n// Returns the token string, expiration time, and any error encountered.\nfunc (r *jwtFileReader) readToken() (string, time.Time, error) {\n\ttokenBytes, err := os.ReadFile(r.tokenFilePath)\n\tif err != nil {\n\t\treturn \"\", time.Time{}, fmt.Errorf(\"%v: %w\", err, errTokenFileAccess)\n\t}\n\n\ttoken := strings.TrimSpace(string(tokenBytes))\n\tif token == \"\" {\n\t\treturn \"\", time.Time{}, fmt.Errorf(\"token file %q is empty: %w\", r.tokenFilePath, errJWTValidation)\n\t}\n\n\texp, err := r.extractExpiration(token)\n\tif err != nil {\n\t\treturn \"\", time.Time{}, fmt.Errorf(\"token file %q: %v: %w\", r.tokenFilePath, err, errJWTValidation)\n\t}\n\n\treturn token, exp, nil\n}\n\nconst tokenDelim = \".\"\n\n// extractClaimsRaw returns the JWT's claims part as raw string. Even though the\n// header and signature are not used, it still expects that the input string to\n// be well-formed (ie comprised of exactly three parts, separated by a dot\n// character).\nfunc extractClaimsRaw(s string) (string, bool) {\n\t_, s, ok := strings.Cut(s, tokenDelim)\n\tif !ok { // no period found\n\t\treturn \"\", false\n\t}\n\tclaims, s, ok := strings.Cut(s, tokenDelim)\n\tif !ok { // only one period found","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/credentials/jwt/file_reader.go#L44-L80","documentation":"Returned by jwtFileReader.readToken wrapping any failure from extractExpiration (the %v is the inner error: bad format, decode failure, missing claims, or expiry). The sentinel errJWTValidation maps to codes.Unauthenticated at the call site. This is the generic wrapper for a structurally invalid or expired JWT in the file.","triggerScenarios":"The file contains a string that is not a well-formed JWT (no exp claim, malformed base64, expired token); the file was overwritten mid-rotation with a partial token; the wrong kind of token (opaque instead of JWT) was written.","commonSituations":"Token rotation leaving a truncated file; using an opaque OAuth access token where a JWT was expected; clock skew making a valid token appear expired; misconfigured token broker.","solutions":["Open the token file and decode the payload (jwt.io, base64 -d) to inspect the claims and exp.","Ensure the token writer emits a complete JWT atomically (write to a temp file then rename).","Sync the system clock (ntp/chrony) if expiry looks wrong.","Regenerate the token and verify it parses with the same library before deploying."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-parse the token with the same logic to give a precise startup error.\nfunc validateTokenFile(path string) error {\n    b, err := os.ReadFile(path)\n    if err != nil {\n        return err\n    }\n    tok := strings.TrimSpace(string(b))\n    parts := strings.Split(tok, \".\")\n    if len(parts) != 3 {\n        return fmt.Errorf(\"token in %q is not a 3-part JWT\", path)\n    }\n    payload, err := base64.RawURLEncoding.DecodeString(parts[1])\n    if err != nil {\n        return fmt.Errorf(\"token payload decode: %w\", err)\n    }\n    var c struct{ Exp int64 `json:\"exp\"` }\n    if err := json.Unmarshal(payload, &c); err != nil {\n        return fmt.Errorf(\"token payload json: %w\", err)\n    }\n    if c.Exp == 0 {\n        return fmt.Errorf(\"token has no exp claim\")\n    }\n    if time.Unix(c.Exp, 0).Before(time.Now()) {\n        return fmt.Errorf(\"token expired\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate the token with a local parser at startup to surface the precise sub-error.","Ensure the token writer writes atomically (temp file + rename).","Sync the system clock to avoid false expiry."],"tags":["grpc","jwt","validation","credentials"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}