hashicorp/nomad · error

invalid PrivateKey: %w

Error message

invalid PrivateKey: %w

What it means

Wraps the validation failure of the OIDCClientAssertion PrivateKey sub-object when the auth client assertion KeySource is `private_key`. The nested PrivateKey.Validate() failed (missing key/cert material or unreadable content), and Nomad re-raises it with the `invalid PrivateKey:` prefix. It means the configured private key for OIDC client authentication is invalid.

Source

Thrown at nomad/structs/acl.go:1781

func (c *OIDCClientAssertion) IsSet() bool {
	return c != nil && c.KeySource != ""
}

func (c *OIDCClientAssertion) Validate() error {
	if c == nil {
		return nil
	}
	if len(c.Audience) == 0 || c.Audience[0] == "" {
		return errors.New("missing Audience")
	}
	switch c.KeySource {
	case OIDCKeySourceNomad:
	case OIDCKeySourcePrivateKey:
		if c.PrivateKey == nil {
			return errors.New("PrivateKey is required for `private_key` KeySource")
		}
		if err := c.PrivateKey.Validate(); err != nil {
			return fmt.Errorf("invalid PrivateKey: %w", err)
		}
	case OIDCKeySourceClientSecret:
		if c.ClientSecret == "" {
			return errors.New("OIDCClientSecret is required for `client_secret` KeySource")
		}
	default:
		return fmt.Errorf("invalid KeySource %q", c.KeySource)
	}
	return nil
}

type OIDCClientAssertionKeyIDHeader string

const (
	OIDCClientAssertionHeaderKid     OIDCClientAssertionKeyIDHeader = "kid"
	OIDCClientAssertionHeaderX5t     OIDCClientAssertionKeyIDHeader = "x5t"
	OIDCClientAssertionHeaderX5tS256 OIDCClientAssertionKeyIDHeader = "x5t#S256"
)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Read the wrapped %w message to see which nested field failed, then supply exactly one valid private key (PemKey or PemKeyFile).
  2. Ensure the PEM block is complete including -----BEGIN/END----- lines.
  3. Verify the referenced file exists and is readable by the Nomad server.
  4. Run the provider validation again after fixing.

Example fix

// before
key_source = "private_key"
pem_key = ""
pem_key_file = "/etc/ssl/client.key"
pem_cert_file = "/etc/ssl/client.crt"

// after
key_source = "private_key"
pem_key_file = "/etc/ssl/client.key" // complete, valid PEM only
Defensive patterns

Strategy: validation

Validate before calling

func checkPrivateKey(k *OIDCClientAssertionPrivateKey) error {
    if k == nil || (k.PemKey == "" && k.PemKeyFile == "") {
        return errors.New("private key material required")
    }
    if k.PemKey != "" && k.PemKeyFile != "" {
        return errors.New("set only one of pem_key or pem_key_file")
    }
    if k.PemKey != "" && !strings.Contains(k.PemKey, "-----BEGIN") {
        return errors.New("pem_key is not a valid PEM block")
    }
    return nil
}

Type guard

func hasValidPEM(s string) bool {
    return strings.Contains(s, "-----BEGIN") && strings.Contains(s, "-----END")
}

Try / catch

if err := client.ACLAuthMethods().Upsert(...); err != nil {
    var wrapped interface{ Unwrap() error }
    if errors.As(err, &target) && strings.Contains(err.Error(), "invalid PrivateKey") {
        // surface nested PrivateKey validation detail to operator
    }
}

Prevention

When it happens

Trigger: Validating an OIDC provider/auth-method whose OIDCClientAssertion has KeySource set to "private_key" but whose PrivateKey fails Validate() - e.g. nil/empty PemKey and PemKeyFile, both set simultaneously, or malformed PEM data.

Common situations: Paste of PEM content with missing header/footer lines; specifying both pem_key and pem_key_file; uploading a truncated key; referencing a key file path that was emptied by templating.

Related errors


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