grpc/grpc-go · error

required field SubjectTokenPath is not specified

Error message

required field SubjectTokenPath is not specified

What it means

Returned by sts.validateOptions when Options.SubjectTokenPath is empty. The subject token is the security token (e.g., a Kubernetes service-account JWT or a VM identity token) that the STS client exchanges for an access token; without a file path to read it from, the token exchange cannot be constructed.

Solutions

  1. Set Options.SubjectTokenPath to the filesystem path of the subject token file (e.g., /var/run/secrets/tokens/token).
  2. Confirm the file is readable by the process before creating the credentials.
  3. If the path is templated from config, validate the template resolved to a non-empty value.

Example fix

// before
opts := sts.Options{
    TokenExchangeServiceURI: "https://sts.googleapis.com/v1/token",
    // SubjectTokenPath missing
}
// after
opts := sts.Options{
    TokenExchangeServiceURI: "https://sts.googleapis.com/v1/token",
    SubjectTokenPath:        "/var/run/secrets/tokens/sa-token",
}
Defensive patterns

Strategy: validation

Validate before calling

if opts.SubjectTokenPath == "" {
    return fmt.Errorf("SubjectTokenPath must be set to a readable token file")
}
if _, err := os.Stat(opts.SubjectTokenPath); err != nil {
    return fmt.Errorf("subject token file not accessible: %w", err)
}

Prevention

When it happens

Trigger: Calling sts.NewCredentials with an Options struct where SubjectTokenPath is not set (empty string). This is a required field validated after the TokenExchangeServiceURI check.

Common situations: Config files that map STS options from bootstrap data but omit the subject_token_path key. Workload-identity setups where the path is supposed to be injected by the runtime but the injection failed or the key name changed between versions.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/2a666df32e4d54d7. Report an issue: GitHub.

Appendix: source

Thrown at credentials/sts/sts.go:231

// validateOptions performs the following validation checks on opts:
// - tokenExchangeServiceURI is not empty
// - tokenExchangeServiceURI is a valid URI with a http(s) scheme
// - subjectTokenPath and subjectTokenType are not empty.
func validateOptions(opts Options) error {
	if opts.TokenExchangeServiceURI == "" {
		return errors.New("empty token_exchange_service_uri in options")
	}
	u, err := url.Parse(opts.TokenExchangeServiceURI)
	if err != nil {
		return err
	}
	if u.Scheme != "http" && u.Scheme != "https" {
		return fmt.Errorf("scheme is not supported: %q. Only http(s) is supported", u.Scheme)
	}

	if opts.SubjectTokenPath == "" {
		return errors.New("required field SubjectTokenPath is not specified")
	}
	if opts.SubjectTokenType == "" {
		return errors.New("required field SubjectTokenType is not specified")
	}
	return nil
}

// cachedMetadata returns the cached metadata provided it is not going to
// expire anytime soon.
//
// Caller must hold c.mu.
func (c *callCreds) cachedMetadata() map[string]string {
	now := time.Now()
	// If the cached token has not expired and the lifetime remaining on that
	// token is greater than the minimum value we are willing to accept, go
	// ahead and use it.
	if c.tokenExpiry.After(now) && c.tokenExpiry.Sub(now) > minCachedTokenLifetime {
		return c.tokenMetadata

View on GitHub (pinned to 0c51461d27)