grpc/grpc-go · error
%v: %w
Error message
%v: %w
What it means
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.
Source
Thrown at credentials/jwt/file_reader.go:52
)
// jwtClaims represents the JWT claims structure for extracting expiration time.
type jwtClaims struct {
Exp int64 `json:"exp"`
}
// jwtFileReader handles reading and parsing JWT tokens from files.
// It is safe to call methods on this type concurrently as no state is stored.
type jwtFileReader struct {
tokenFilePath string
}
// readToken reads and parses a JWT token from the configured file.
// Returns the token string, expiration time, and any error encountered.
func (r *jwtFileReader) readToken() (string, time.Time, error) {
tokenBytes, err := os.ReadFile(r.tokenFilePath)
if err != nil {
return "", time.Time{}, fmt.Errorf("%v: %w", err, errTokenFileAccess)
}
token := strings.TrimSpace(string(tokenBytes))
if token == "" {
return "", time.Time{}, fmt.Errorf("token file %q is empty: %w", r.tokenFilePath, errJWTValidation)
}
exp, err := r.extractExpiration(token)
if err != nil {
return "", time.Time{}, fmt.Errorf("token file %q: %v: %w", r.tokenFilePath, err, errJWTValidation)
}
return token, exp, nil
}
const tokenDelim = "."
// extractClaimsRaw returns the JWT's claims part as raw string. Even though theView on GitHub (pinned to 03255a9237)
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.
Example fix
// before
r := &jwtFileReader{tokenFilePath: "token"} // relative, missing
// after
path := "/var/run/secrets/tokens/token"
if _, err := os.Stat(path); err != nil { log.Fatal(err) }
r := &jwtFileReader{tokenFilePath: path} Defensive patterns
Strategy: validation
Validate before calling
// Verify the token file is readable before constructing the credential.
if _, err := os.Stat(tokenFilePath); err != nil {
return fmt.Errorf("token file not accessible: %w", err)
} Type guard
// Detect the file-access sentinel returned by readToken.
func isTokenFileAccessErr(err error) bool {
return errors.Is(err, errTokenFileAccess) // errTokenFileAccess from credentials/jwt
} Try / catch
_, _, err := r.readToken()
if err != nil {
if errors.Is(err, errTokenFileAccess) {
// filesystem problem: fix path/permissions; not retryable as-is.
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- token file %q is empty: %w
- token file access error
- credentials: audience cannot be empty
- token file %q: %v: %w
- no expiration claims
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/5047a124f6042181.
Report an issue: GitHub.