hashicorp/nomad · error

invalid KeyIDHeader

Error message

invalid KeyIDHeader

What it means

KeyIDHeader for a client assertion must be one of "x5t", "x5t#S256" (certificate-derived) or "kid" (when a raw KeyID is used). ErrInvalidKeyIDHeader is returned by Validate() and defensively by lib/auth/oidc hashKeyID when the header name is anything else, since the OIDC provider would not recognize the header.

Source

Thrown at nomad/structs/acl.go:1844

	}
	if k.KeyIDHeader == "" {
		if k.KeyID != "" {
			k.KeyIDHeader = OIDCClientAssertionHeaderKid
		}
		if k.PemCert != "" || k.PemCertFile != "" {
			k.KeyIDHeader = OIDCClientAssertionHeaderX5tS256
		}
	}
}

var (
	ErrMissingClientAssertionKey      = errors.New("missing PemKey or PemKeyFile")
	ErrAmbiguousClientAssertionKey    = errors.New("require only one of PemKey or PemKeyFile")
	ErrMissingClientAssertionKeyID    = errors.New("missing PemCert, PemCertFile, or KeyID")
	ErrAmbiguousClientAssertionKeyID  = errors.New("require only one of PemCert, PemCertFile, or KeyID")
	ErrInvalidClientAssertionKeyPath  = errors.New("invalid PemKeyFile")
	ErrInvalidClientAssertionCertPath = errors.New("invalid PemCertFile")
	ErrInvalidKeyIDHeader             = errors.New("invalid KeyIDHeader")
)

// Validate ensures that one Key and one Cert or KeyID are provided,
// and that the key ID header is valid for the provided KeyID or cert.
func (k *OIDCClientAssertionKey) Validate() error {
	if k == nil {
		return nil
	}

	// mutually exclusive key fields
	// must have key file or base64, but not both
	if k.PemKey == "" && k.PemKeyFile == "" {
		return ErrMissingClientAssertionKey
	}
	if k.PemKey != "" && k.PemKeyFile != "" {
		return ErrAmbiguousClientAssertionKey
	}
	if k.PemKeyFile != "" {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. When using KeyID, set KeyIDHeader to structs.OIDCClientAssertionHeaderKid ("kid")
  2. When using a cert, set KeyIDHeader to "x5t#S256" (preferred) or "x5t"
  3. Fix typos/whitespace in the header string in the auth-method config

Example fix

// before
key := &structs.OIDCClientAssertionKey{
  KeyID: "abc123",
  KeyIDHeader: "Kid",
}
// after
key := &structs.OIDCClientAssertionKey{
  KeyID: "abc123",
  KeyIDHeader: structs.OIDCClientAssertionHeaderKid,
}
Defensive patterns

Strategy: validation

Validate before calling

valid := map[string]bool{
    structs.OIDCClientAssertionHeaderKid: true,
    structs.OIDCClientAssertionHeaderX5t: true,
    structs.OIDCClientAssertionHeaderX5tS256: true,
}
if !valid[key.KeyIDHeader] {
    return fmt.Errorf("KeyIDHeader %q not allowed", key.KeyIDHeader)
}

Try / catch

if err := key.Validate(); err != nil {
    if errors.Is(err, structs.ErrInvalidKeyIDHeader) {
        // set KeyIDHeader to kid, x5t, or x5t#S256
    }
    return err
}

Prevention

When it happens

Trigger: Validate() where KeyID is set but KeyIDHeader != "kid"; or a cert is set with KeyIDHeader not in {"x5t","x5t#S256"}; at runtime, hashKeyID hitting its default branch with an unknown header.

Common situations: Typo'd header values ("x5T", "kid #256"); copying JWT header names from another IdP's docs; older configs written before x5t#S256 support existed.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/9f60ba58faaf78e1. Report an issue: GitHub.