grpc/grpc-go · error

token file %q: %v: %w

Error message

token file %q: %v: %w

What it means

readToken wraps any failure of extractExpiration as 'token file %q: %v: %w' with the path, the inner JWT error, and the errJWTValidation sentinel (file_reader.go:60-63). It is the umbrella error for [175]–[179]: the file was readable and non-empty but its content is not a valid, unexpired JWT.

Source

Thrown at credentials/jwt/file_reader.go:62

	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
// header and signature are not used, it still expects that the input string to
// be well-formed (ie comprised of exactly three parts, separated by a dot
// character).
func extractClaimsRaw(s string) (string, bool) {
	_, s, ok := strings.Cut(s, tokenDelim)
	if !ok { // no period found
		return "", false
	}
	claims, s, ok := strings.Cut(s, tokenDelim)
	if !ok { // only one period found

View on GitHub (pinned to 03255a9237)

Solutions

  1. Inspect the inner %v to identify the specific failure ([175]–[179]) and apply the matching fix.
  2. Confirm the file actually contains a JWT (header.payload.signature) and not another credential type.
  3. Regenerate/refresh the token so it has a valid future exp claim.
  4. Validate the file content with a JWT decoder (e.g. jwt.io or a local decoder) before pointing the credential at it.

Example fix

// before: pointed at the wrong credential file
r := &jwtFileReader{tokenFilePath: "/etc/creds/oauth_access_token"}

// after: pointed at the ID-token file with a valid JWT
r := &jwtFileReader{tokenFilePath: "/etc/creds/id_token_jwt"}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-decode the JWT to fail with a clear message before the credential uses it.
if _, _, err := r.readToken(); err != nil {
    return fmt.Errorf("token file contents are not a valid JWT: %w", err)
}

Type guard

func isJWTValidationErr(err error) bool {
    return errors.Is(err, errJWTValidation)
}

Try / catch

_, _, err := r.readToken()
if err != nil {
    if errors.Is(err, errJWTValidation) {
        // see the inner %v for the specific JWT defect ([175]-[179]).
    }
    return err
}

Prevention

When it happens

Trigger: extractExpiration returns a non-nil error — token is not three dot-separated parts, base64 decode fails, JSON unmarshal fails, no exp claim, or the exp claim is in the past. The inner %v identifies which sub-case.

Common situations: Wrong file used (OAuth access token instead of ID token, a raw key, a refresh token), a JWT minted without exp, a stale token whose exp passed, or copy/paste corruption of the token string.

Related errors


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