grpc/grpc-go · error

required field SubjectTokenType is not specified

Error message

required field SubjectTokenType is not specified

What it means

Returned by sts.validateOptions when Options.SubjectTokenType is empty. SubjectTokenType is an RFC 8693 identifier (e.g., urn:ietf:params:oauth:token-type:jwt) that tells the STS server what kind of token the subject_token is. Without it the token exchange server cannot interpret the subject token.

Solutions

  1. Set Options.SubjectTokenType to the correct URN for your subject token type.
  2. For JWT subject tokens (e.g., Kubernetes SA tokens), use urn:ietf:params:oauth:token-type:jwt.
  3. For opaque or SAML tokens, use the corresponding URN from RFC 8693 section 3.

Example fix

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

Strategy: validation

Validate before calling

if opts.SubjectTokenType == "" {
    return fmt.Errorf("SubjectTokenType must be set (e.g., urn:ietf:params:oauth:token-type:jwt)")
}

Prevention

When it happens

Trigger: Calling sts.NewCredentials with an Options struct where SubjectTokenType is not set. This is the last of the three required-field checks in validateOptions (after URI and SubjectTokenPath).

Common situations: Copy-paste from examples that omit the token type, or config that relies on a default value (there is no default). Developers unfamiliar with RFC 8693 URN identifiers may leave it blank thinking it is optional.

Related errors


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

Appendix: source

Thrown at credentials/sts/sts.go:234

// - 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
	}
	return nil
}

View on GitHub (pinned to 0c51461d27)