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 the

View on GitHub (pinned to 03255a9237)

Solutions

  1. Verify the path exists and is readable by the process uid: stat the file and read it from the same user.
  2. Use an absolute path for tokenFilePath; avoid paths relative to a moving working directory.
  3. In k8s, confirm the Secret/ConfigMap volume is mounted and the key name matches.
  4. 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

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


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/5047a124f6042181. Report an issue: GitHub.