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.

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.

Appendix: 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)