hashicorp/nomad · error

invalid PemKeyFile

Error message

invalid PemKeyFile

What it means

When the client assertion private key is given as a file (PemKeyFile), the path must be absolute. ErrInvalidClientAssertionKeyPath is wrapped (fmt.Errorf %w) with 'must be absolute; got: <path>' when a relative path is supplied, because the key file is read by agents whose working directory is not the operator's.

Source

Thrown at nomad/structs/acl.go:1842

	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 != "" {
		return ErrAmbiguousClientAssertionKey

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Change PemKeyFile to an absolute path like /etc/nomad/tls/client.key
  2. Expand ~ and relative segments in your config tooling before applying
  3. Verify with path.IsAbs in scripts that generate the config

Example fix

// before
key := &structs.OIDCClientAssertionKey{
  PemKeyFile: "./tls/client.key",
}
// after
key := &structs.OIDCClientAssertionKey{
  PemKeyFile: "/etc/nomad/tls/client.key",
}
Defensive patterns

Strategy: validation

Validate before calling

if key.PemKeyFile != "" && !path.IsAbs(key.PemKeyFile) {
    abs, err := filepath.Abs(key.PemKeyFile)
    if err != nil { return err }
    key.PemKeyFile = abs
}

Try / catch

if err := key.Validate(); err != nil {
    var wrapped error
    if errors.As(err, &wrapped) && strings.Contains(err.Error(), ErrInvalidClientAssertionKeyPath.Error()) {
        // convert to an absolute path and retry validation
    }
    return err
}

Prevention

When it happens

Trigger: Validate() where k.PemKeyFile != "" and path.IsAbs(k.PemKeyFile) is false, e.g. "tls/client.key" or "./client.key".

Common situations: Configs written relative to a deploy directory; paths copied from a local dev shell; templating that emits ~ or relative refs; agents running under systemd with different WorkingDirectory.

Related errors


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