{"record":{"id":"465a454e90e564b1","repo":"hashicorp/nomad","slug":"require-only-one-of-pemkey-or-pemkeyfile","errorCode":null,"errorMessage":"require only one of PemKey or PemKeyFile","messagePattern":"require only one of PemKey or PemKeyFile","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":1839,"sourceCode":"}\n\nfunc (k *OIDCClientAssertionKey) Canonicalize() {\n\tif k == nil {\n\t\treturn\n\t}\n\tif k.KeyIDHeader == \"\" {\n\t\tif k.KeyID != \"\" {\n\t\t\tk.KeyIDHeader = OIDCClientAssertionHeaderKid\n\t\t}\n\t\tif k.PemCert != \"\" || k.PemCertFile != \"\" {\n\t\t\tk.KeyIDHeader = OIDCClientAssertionHeaderX5tS256\n\t\t}\n\t}\n}\n\nvar (\n\tErrMissingClientAssertionKey      = errors.New(\"missing PemKey or PemKeyFile\")\n\tErrAmbiguousClientAssertionKey    = errors.New(\"require only one of PemKey or PemKeyFile\")\n\tErrMissingClientAssertionKeyID    = errors.New(\"missing PemCert, PemCertFile, or KeyID\")\n\tErrAmbiguousClientAssertionKeyID  = errors.New(\"require only one of PemCert, PemCertFile, or KeyID\")\n\tErrInvalidClientAssertionKeyPath  = errors.New(\"invalid PemKeyFile\")\n\tErrInvalidClientAssertionCertPath = errors.New(\"invalid PemCertFile\")\n\tErrInvalidKeyIDHeader             = errors.New(\"invalid KeyIDHeader\")\n)\n\n// Validate ensures that one Key and one Cert or KeyID are provided,\n// and that the key ID header is valid for the provided KeyID or cert.\nfunc (k *OIDCClientAssertionKey) Validate() error {\n\tif k == nil {\n\t\treturn nil\n\t}\n\n\t// mutually exclusive key fields\n\t// must have key file or base64, but not both\n\tif k.PemKey == \"\" && k.PemKeyFile == \"\" {\n\t\treturn ErrMissingClientAssertionKey","sourceCodeStart":1821,"sourceCodeEnd":1857,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L1821-L1857","documentation":"Nomad's OIDCClientAssertionKey.Validate() requires the private key for a client assertion to be supplied in exactly one way: inline via PemKey or via a file path with PemKeyFile. This sentinel error (ErrAmbiguousClientAssertionKey) is returned when both fields are set, because the library cannot tell which source is authoritative.","triggerScenarios":"Calling Validate() (directly or via ACLAuthMethod upsert/validation endpoints) on an OIDCClientAssertionKey where both PemKey != \"\" and PemKeyFile != \"\".","commonSituations":"Config templates or HCL/JSON job files where an operator pasted both an inline PEM block and a path; tooling that renders both fields from a vault/secret reference; copy-paste from examples that show both options.","solutions":["Remove either the PemKey inline value or the PemKeyFile path so exactly one is set","Keep PemKeyFile for production (secret stays on disk) and clear PemKey","Keep PemKey for in-memory/template-injected setups and clear PemKeyFile"],"exampleFix":"// before\nkey := &structs.OIDCClientAssertionKey{\n  PemKey: string(pemBytes),\n  PemKeyFile: \"/etc/nomad/tls/client.key\",\n}\n// after\nkey := &structs.OIDCClientAssertionKey{\n  PemKeyFile: \"/etc/nomad/tls/client.key\",\n}","handlingStrategy":"validation","validationCode":"if key.PemKey != \"\" && key.PemKeyFile != \"\" {\n    // drop one before calling Validate()\n    key.PemKey = \"\"\n}\nif err := key.Validate(); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := key.Validate(); err != nil {\n    if errors.Is(err, structs.ErrAmbiguousClientAssertionKey) {\n        // fix config: keep exactly one of PemKey / PemKeyFile\n    }\n    return err\n}","preventionTips":["Configure exactly one key source per environment; drive it from a single template variable","Prefer PemKeyFile in production so secrets live on disk","Run key.Validate() in unit tests over all rendered configs"],"tags":["nomad","oidc","client-assertion","config-validation"],"backgroundTag":"ambiguous-config-field","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}