{"record":{"id":"5047a124f6042181","repo":"grpc/grpc-go","slug":"v-w","errorCode":null,"errorMessage":"%v: %w","messagePattern":"%v: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/jwt/file_reader.go","lineNumber":52,"sourceCode":")\n\n// jwtClaims represents the JWT claims structure for extracting expiration time.\ntype jwtClaims struct {\n\tExp int64 `json:\"exp\"`\n}\n\n// jwtFileReader handles reading and parsing JWT tokens from files.\n// It is safe to call methods on this type concurrently as no state is stored.\ntype jwtFileReader struct {\n\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","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/jwt/file_reader.go#L34-L70","documentation":"jwtFileReader.readToken wraps the error from os.ReadFile(r.tokenFilePath) as '%v: %w' with the errTokenFileAccess sentinel (file_reader.go:50-52). The OS error describes what went wrong (not exist, permission denied, …); the sentinel lets callers detect 'token file access error' via errors.Is.","triggerScenarios":"The configured token file path does not exist, is unreadable due to permissions, is on an unmounted volume, the path is relative to the wrong working directory, or the file is a symlink to a missing target.","commonSituations":"Kubernetes Secret/ConfigMap not mounted at the expected path, GOOGLE_APPLICATION_TOKEN_FILE pointing at a stale path, container working directory differs from where the token was written, file owned by root while the process runs as non-root.","solutions":["Verify the path exists and is readable by the process uid: stat the file and read it from the same user.","Use an absolute path for tokenFilePath; avoid paths relative to a moving working directory.","In k8s, confirm the Secret/ConfigMap volume is mounted and the key name matches.","Check filesystem permissions and that any volume mount completed before the process starts."],"exampleFix":"// before\nr := &jwtFileReader{tokenFilePath: \"token\"} // relative, missing\n\n// after\npath := \"/var/run/secrets/tokens/token\"\nif _, err := os.Stat(path); err != nil { log.Fatal(err) }\nr := &jwtFileReader{tokenFilePath: path}","handlingStrategy":"validation","validationCode":"// Verify the token file is readable before constructing the credential.\nif _, err := os.Stat(tokenFilePath); err != nil {\n    return fmt.Errorf(\"token file not accessible: %w\", err)\n}","typeGuard":"// Detect the file-access sentinel returned by readToken.\nfunc isTokenFileAccessErr(err error) bool {\n    return errors.Is(err, errTokenFileAccess) // errTokenFileAccess from credentials/jwt\n}","tryCatchPattern":"_, _, err := r.readToken()\nif err != nil {\n    if errors.Is(err, errTokenFileAccess) {\n        // filesystem problem: fix path/permissions; not retryable as-is.\n    }\n    return err\n}","preventionTips":["Use absolute token file paths.","Stat the file at startup to catch missing/unreadable paths early.","In k8s, confirm the Secret/ConfigMap volume and key are mounted.","Run the process as a user that can read the token file."],"tags":["grpc","jwt","filesystem","configuration","credentials"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}