grpc/grpc-go · error
%v: %w
Error message
%v: %w
What it means
Returned by jwtFileReader.readToken when os.ReadFile fails to read the token file (the %v is the OS error and the wrapped sentinel is errTokenFileAccess). The call site in jwtTokenFileCallCreds converts this into a gRPC codes.Unavailable status, since the file (often a mounted token volume) is temporarily unreadable.
Solutions
- Check the exact token file path exists and is readable by the process user (ls -l, stat).
- In Kubernetes, ensure the ServiceAccount token is projected and add an initContainer or readiness probe that waits for the file.
- Use an absolute path for tokenFilePath.
- Run the binary as a user/group that has read permission on the token file.
Example fix
// before
creds, err := jwt.NewTokenFileCallCredentials("token.jwt")
// after
creds, err := jwt.NewTokenFileCallCredentials("/var/run/secrets/tokens/my-sa-token") Defensive patterns
Strategy: validation
Validate before calling
// Validate readability before constructing the credential.
func checkTokenFile(path string) error {
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("token file %q: %w", path, err)
}
if info.Mode().Perm()&0400 == 0 {
return fmt.Errorf("token file %q not readable", path)
}
return nil
}
if err := checkTokenFile(tokenPath); err != nil {
log.Fatal(err)
} Try / catch
// The RPC surfaces codes.Unavailable; detect and fail fast on startup:
if status.Code(err) == codes.Unavailable && strings.Contains(err.Error(), errTokenFileAccess.Error()) {
log.Fatal("token file unreadable; check path and permissions")
} Prevention
- Stat the token file at startup and fail fast.
- Use absolute paths from a single config source.
- In Kubernetes, ensure the projected token volume is mounted before the main container starts.
When it happens
Trigger: The configured token file path does not exist; the process lacks read permission on the file; the file is on a volume that has not yet been mounted (Kubernetes projected service-account token) or has been rotated away; a path typo or wrong working directory.
Common situations: Kubernetes pod starting before the projected token volume is mounted; running as a user without read access to /var/run/secrets/...; relative path resolved against an unexpected working directory; NFS/containerd volume mount race.
Related errors
- token file access error
- token file is empty
- cannot send secure credentials on an insecure connection
- credentials: audience cannot be empty
- credentials: failed to read the service account key file
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/5047a124f6042181.
Report an issue: GitHub.
Appendix: 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 0c51461d27)