grpc/grpc-go · error

tokenFilePath cannot be empty

Error message

tokenFilePath cannot be empty

What it means

Thrown by jwt.NewTokenFileCallCredentials when the supplied tokenFilePath argument is the empty string (token_file_call_creds.go:57). The JWT-from-file call-credentials implementation (gRFC A97) needs a concrete filesystem path to read and refresh Bearer tokens from, so an empty path cannot produce a usable credential. It is a construction-time programmer error, not a runtime transport failure.

Source

Thrown at credentials/jwt/token_file_call_creds.go:57

type jwtTokenFileCallCreds struct {
	fileReader      *jwtFileReader
	backoffStrategy backoff.Strategy

	// cached data protected by mu
	mu               sync.Mutex
	cachedAuthHeader string    // "Bearer " + token
	cachedExpiry     time.Time // Slightly less than actual expiration time
	cachedError      error     // Error from last failed attempt
	retryAttempt     int       // Current retry attempt number
	nextRetryTime    time.Time // When next retry is allowed
	pendingRefresh   bool      // Whether a refresh is currently in progress
}

// NewTokenFileCallCredentials creates PerRPCCredentials that reads JWT tokens
// from the specified file path.
func NewTokenFileCallCredentials(tokenFilePath string) (credentials.PerRPCCredentials, error) {
	if tokenFilePath == "" {
		return nil, fmt.Errorf("tokenFilePath cannot be empty")
	}

	creds := &jwtTokenFileCallCreds{
		fileReader:      &jwtFileReader{tokenFilePath: tokenFilePath},
		backoffStrategy: backoff.DefaultExponential,
	}

	return creds, nil
}

// GetRequestMetadata gets the current request metadata, refreshing tokens if
// required. This implementation follows the PerRPCCredentials interface.  The
// tokens will get automatically refreshed if they are about to expire or if
// they haven't been loaded successfully yet.
// If it's not possible to extract a token from the file, UNAVAILABLE is
// returned.
// If the token is extracted but invalid, then UNAUTHENTICATED is returned.
// If errors are encoutered, a backoff is applied before retrying.

View on GitHub (pinned to 03255a9237)

Solutions

  1. Pass a non-empty, absolute path to a readable file when calling NewTokenFileCallCredentials.
  2. Validate the config source (env var / flag) for emptiness and fail fast at startup with a clear message.
  3. If the path comes from a secret mount, confirm the volume/secret is mounted and the path key is correct.

Example fix

// before
creds, err := jwt.NewTokenFileCallCredentials(os.Getenv("JWT_TOKEN_FILE"))

// after
path := os.Getenv("JWT_TOKEN_FILE")
if path == "" {
    log.Fatal("JWT_TOKEN_FILE must be set")
}
creds, err := jwt.NewTokenFileCallCredentials(path)
Defensive patterns

Strategy: validation

Validate before calling

path := os.Getenv("JWT_TOKEN_FILE")
if strings.TrimSpace(path) == "" {
    log.Fatal("JWT_TOKEN_FILE must be a non-empty file path")
}
if _, err := os.Stat(path); err != nil {
    log.Fatalf("token file not accessible: %v", err)
}
creds, err := jwt.NewTokenFileCallCredentials(path)
if err != nil { log.Fatal(err) }

Type guard

// PerRPCCredentials must be non-nil and transport-secure; guard at construction:
func mustJWTCallCreds(path string) credentials.PerRPCCredentials {
    if path == "" { panic("empty tokenFilePath") }
    c, err := jwt.NewTokenFileCallCredentials(path)
    if err != nil { panic(err) }
    return c
}

Prevention

When it happens

Trigger: Calling jwt.NewTokenFileCallCredentials("") directly, or passing an unset/zero-value string variable (e.g. an env var like os.Getenv("JWT_TOKEN_FILE") that was never exported, or a struct field left as its default) into the constructor.

Common situations: Deployment env where the JWT token-file path is injected via an env var or secret mount that is missing/misspelled; tests that forget to set the path; config loaders that swallow the missing-key case and return "" instead of failing.

Related errors


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