gofr-dev/gofr · critical

failed to read token from %s: %w

Error message

failed to read token from %s: %w

What it means

NewFileTokenAuthConfig wraps any failure from readToken (missing file, permission error, or empty token file) as "failed to read token from %s: %w" (file_token_auth.go:98). The token is read eagerly at construction so a bad token path or unreadable file aborts startup instead of failing lazily on the first upstream HTTP call.

Source

Thrown at pkg/gofr/service/file_token_auth.go:98

// The token file is read eagerly: a missing or empty file returns an error so
// misconfiguration is caught at startup rather than at the first upstream call.
// The logger is supplied automatically by NewHTTPService via the
// Observable hook; until it arrives, background-refresh failures are
// silent.
func NewFileTokenAuthConfig(opts ...FileTokenAuthOption) (*FileTokenAuthConfig, error) {
	f := &FileTokenAuthConfig{
		tokenFilePath:   DefaultTokenFilePath,
		refreshInterval: defaultRefreshInterval,
		done:            make(chan struct{}),
	}

	for _, opt := range opts {
		opt(f)
	}

	token, err := readToken(f.tokenFilePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read token from %s: %w", f.tokenFilePath, err)
	}

	f.token = token

	go f.refreshLoop()

	return f, nil
}

// AddOption implements Options.
func (f *FileTokenAuthConfig) AddOption(h HTTP) HTTP {
	return &fileTokenDecorator{source: f, HTTP: h}
}

// SetLogger implements Observable. NewHTTPService calls this with the
// HTTP service's logger so background-refresh failures can be surfaced at WARN
// level. If l does not satisfy logging.Logger (the richer interface with
// Warnf), the logger stays unset and refresh failures remain silent rather

View on GitHub (pinned to 187eb24962)

Solutions

  1. Read the wrapped cause (os.PathError vs 'token file is empty') to decide between fixing the path and fixing file contents.
  2. Mount the service-account projected volume or set automountServiceAccountToken: true in the pod spec.
  3. Pass the correct file via WithTokenFilePath for non-K8s or custom mounts.
  4. Guard construction at startup so the process fails fast and orchestrators restart it once the volume appears.

Example fix

// before
auth, err := service.NewFileTokenAuthConfig() // default path missing outside K8s
// after
if _, statErr := os.Stat(tokenPath); statErr == nil {
    auth, err = service.NewFileTokenAuthConfig(service.WithTokenFilePath(tokenPath))
}
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(tokenPath); err != nil {
	return fmt.Errorf("token file %s not available: %w", tokenPath, err)
}

Type guard

var pathErr *os.PathError
if errors.As(err, &pathErr) { /* missing/unreadable file vs empty content */ }

Try / catch

cfg, err := service.NewFileTokenAuthConfig(opts...)
if err != nil {
	if errors.Is(err, errEmptyTokenFile) { /* fix file contents */ }
	else if errors.As(err, &pathErr) { /* fix path/mount */ }
	return err // fail startup fast
}

Prevention

When it happens

Trigger: Calling NewFileTokenAuthConfig — directly or via main / NewHTTPService options / tests — when the file at the configured (or default K8s) token path does not exist, is unreadable, or is empty; os.ReadFile returns its error and readToken's errEmptyTokenFile is wrapped here.

Common situations: Running outside Kubernetes with the default /var/run/secrets/kubernetes.io/serviceaccount/token path absent; typo in WithTokenFilePath; token volume not mounted (automountServiceAccountToken: false); file permissions after a security hardening change.

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/f664c523679d72af. Report an issue: GitHub.