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
- When using KeyID, set KeyIDHeader to structs.OIDCClientAssertionHeaderKid ("kid")
- When using a cert, set KeyIDHeader to "x5t#S256" (preferred) or "x5t"
- 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
- Use the exported OIDCClientAssertionHeader* constants, never raw strings
- Remember kid pairs with KeyID; x5t/x5t#S256 pair with certs
- Check your IdP's documented assertion header support before choosing
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
- failed to build client_assertion jwt: %w
- failed to serialize client_assertion jwt: %w
- no auth method config or client assertion
- missing Audience
- PrivateKey is required for `private_key` KeySource
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/9f60ba58faaf78e1.
Report an issue: GitHub.