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 (credentials/sts/sts.go:224) when opts.SubjectTokenPath is empty. The subject token is the file containing the identity token of the party on whose behalf the exchange is made; it is marked Required in the Options struct comment (line 96). Without it the STS request cannot include subject_token, so NewCredentials refuses to construct.

Source

Thrown at credentials/sts/sts.go:224

// 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 03255a9237)

Solutions

  1. Set Options.SubjectTokenPath to the absolute path of the file containing your subject (identity) token.
  2. Verify the path exists and is readable before calling NewCredentials (see also error 20).
  3. Check the returned error from NewCredentials and surface it in startup logs.

Example fix

// before
c, _ := sts.NewCredentials(sts.Options{
    TokenExchangeServiceURI: "https://sts.googleapis.com/v1/token",
    SubjectTokenType:        "urn:ietf:params:oauth:token-type:jwt",
}) // SubjectTokenPath missing

// after
c, err := sts.NewCredentials(sts.Options{
    TokenExchangeServiceURI: "https://sts.googleapis.com/v1/token",
    SubjectTokenPath:        "/var/run/secrets/subject-token",
    SubjectTokenType:       "urn:ietf:params:oauth:token-type:jwt",
})
Defensive patterns

Strategy: validation

Validate before calling

if opts.SubjectTokenPath == "" {
    return errors.New("SubjectTokenPath must be set to a readable token file")
}
if fi, err := os.Stat(opts.SubjectTokenPath); err != nil || fi.IsDir() {
    return fmt.Errorf("SubjectTokenPath %q not usable: %w", opts.SubjectTokenPath, err)
}

Try / catch

c, err := sts.NewCredentials(opts)
if err != nil {
    log.Fatalf("cannot build STS creds: %v", err)
}

Prevention

When it happens

Trigger: Calling sts.NewCredentials with SubjectTokenPath unset (or empty). The check runs only inside NewCredentials/validateOptions, so this surfaces immediately at construction time.

Common situations: Config loaded from a file/env that omits the subject token file path (common when migrating from in-memory tokens to file-based tokens); path field named differently in config vs struct; expecting a default but none exists.

Related errors


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