jaegertracing/jaeger · error

failed to read token file: %w

Error message

failed to read token file: %w

What it means

The cached file token loader in internal/auth/tokenloader.go:42 wraps os.ReadFile errors with this message when it cannot read the bearer-token file at the given path. The loader caches the token for a configurable interval and re-reads the file when the interval expires, so this error can occur on the first read and on later reloads. On the initial load it is further wrapped as "failed to get token from file" by TokenProviderWithTime; on later reloads it is only logged as a warning and the last cached token is used.

Source

Thrown at internal/auth/tokenloader.go:42

		lastRead    time.Time
	)

	return func() (string, error) {
		mu.Lock()
		defer mu.Unlock()

		now := timeFn()

		// Special case: interval = 0 means "never reload after first load"
		// Otherwise reload only if `interval` time has passed since last load.
		if !lastRead.IsZero() && (interval == 0 || now.Sub(lastRead) < interval) {
			return cachedToken, nil
		}

		// Read from file
		b, err := os.ReadFile(filepath.Clean(path))
		if err != nil {
			return "", fmt.Errorf("failed to read token file: %w", err)
		}

		cachedToken = strings.TrimRight(string(b), "\r\n")
		lastRead = now
		return cachedToken, nil
	}
}

// TokenProvider creates a token provider that handles file loading and error handling consistently.
func TokenProvider(path string, interval time.Duration, logger *zap.Logger) (func() string, error) {
	return TokenProviderWithTime(path, interval, logger, time.Now) // Use real time.Now in production
}

// TokenProviderWithTime creates a token provider with injectable time (for testing)
func TokenProviderWithTime(path string, interval time.Duration, logger *zap.Logger, timeFn func() time.Time) (func() string, error) {
	loader := cachedFileTokenLoader(path, interval, timeFn)

	// current token load

View on GitHub (pinned to 806f444784)

Solutions

  1. Verify the configured path exists and is readable by the process (ls -l <path> as the same user); fix the path or the secret mount
  2. If the file is rotated, rotate atomically (write new file, rename into place) instead of removing then creating it
  3. Set interval=0 to disable reloading so the loader never re-reads after the first successful load
  4. Recreate the token file and restart the process if the initial load failed, since TokenProvider construction aborts on it

Example fix

// before
// token file path: /var/run/secrets/token  (file absent)
// after
// $ ls -l /var/run/secrets/token
// -r-------- 1 app app 215 ... /var/run/secrets/token
// or set interval to 0 to never reload after the first successful read
Defensive patterns

Strategy: fallback

Validate before calling

// check readability before relying on the provider
if info, err := os.Stat(tokenPath); err != nil {
	return fmt.Errorf("token file %s not accessible: %w", tokenPath, err)
} else if info.IsDir() {
	return fmt.Errorf("token path %s is a directory", tokenPath)
}

Try / catch

tokenFn, err := auth.TokenProvider(tokenPath, interval, logger)
if err != nil {
	// on reload failures the provider itself falls back to the last cached token,
	// so only construction errors need handling here
	logger.Error("token auth unavailable", zap.Error(err))
	return err
}

Prevention

When it happens

Trigger: os.ReadFile(filepath.Clean(path)) failing because the file does not exist (ENOENT), the process lacks read permission (EACCES), the path is a directory, or — on a reload after the cache interval elapsed — the file was deleted or rotated away between reads.

Common situations: Kubernetes secret mounted at a different path than configured, or the volume not yet populated at startup; credential rotation that briefly removes the file; running locally without the token file the deployment config expects; wrong relative vs absolute path.

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/eede16605b94e6a1. Report an issue: GitHub.