hashicorp/nomad · error

require only one of PemCert, PemCertFile, or KeyID

Error message

require only one of PemCert, PemCertFile, or KeyID

What it means

Exactly one key-identification mechanism must be chosen for a client assertion. ErrAmbiguousClientAssertionKeyID (ErrMissingClientAssertionKeyID's sibling) is returned when more than one of PemCert, PemCertFile, or KeyID is set, since Nomad cannot decide which identifier to put in the JWT header.

Source

Thrown at nomad/structs/acl.go:1841

func (k *OIDCClientAssertionKey) Canonicalize() {
	if k == nil {
		return
	}
	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 != "" {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Keep only one identifier: prefer PemCertFile for file-based setups
  2. If using a precomputed KeyID, clear PemCert and PemCertFile and set KeyIDHeader appropriately
  3. If using inline cert, clear PemCertFile and KeyID

Example fix

// before
key := &structs.OIDCClientAssertionKey{
  PemCertFile: "/etc/nomad/tls/client.crt",
  KeyID: "abc123",
  KeyIDHeader: structs.OIDCClientAssertionHeaderKid,
}
// after
key := &structs.OIDCClientAssertionKey{
  KeyID: "abc123",
  KeyIDHeader: structs.OIDCClientAssertionHeaderKid,
}
Defensive patterns

Strategy: validation

Validate before calling

set := 0
for _, v := range []string{key.PemCert, key.PemCertFile, key.KeyID} {
    if v != "" { set++ }
}
if set > 1 { return errors.New("choose exactly one of PemCert, PemCertFile, KeyID") }

Try / catch

if err := key.Validate(); err != nil {
    if errors.Is(err, structs.ErrAmbiguousClientAssertionKeyID) {
        // strip the redundant identifier fields
    }
    return err
}

Prevention

When it happens

Trigger: Validate() where PemCert is set together with PemCertFile or KeyID; or PemCertFile set together with PemCert or KeyID; or KeyID set together with PemCert or PemCertFile.

Common situations: Operator supplies the full chain in a file but also pastes a leaf cert inline; automation computes KeyID while the config still carries PemCertFile; gradual refactors leaving stale fields populated.

Related errors


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