{"record":{"id":"e0ddf021658ea919","repo":"hashicorp/nomad","slug":"missing-pemkey-or-pemkeyfile","errorCode":null,"errorMessage":"missing PemKey or PemKeyFile","messagePattern":"missing PemKey or PemKeyFile","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":1838,"sourceCode":"\treturn n\n}\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 == \"\" {","sourceCodeStart":1820,"sourceCodeEnd":1856,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L1820-L1856","documentation":"For private_key client assertions, the signing key must be supplied either inline (PemKey) or via a file path (PemKeyFile). The exported sentinel error ErrMissingClientAssertionKey is returned by the key Validate when both are empty.","triggerScenarios":"Submitting an OIDCClientAssertionKey with both PemKey and PemKeyFile empty (e.g. only KeyID/KeyIDHeader set) during auth method validation.","commonSituations":"Configuring only the key ID and header metadata assuming the key is fetched elsewhere; mounting failures where the file path was configured but the code path reads PemKey first; hand-written JSON omitting both key fields.","solutions":["Set exactly one of PemKey (PEM contents) or PemKeyFile (path) on the key config — not both, which yields ErrAmbiguousClientAssertionKey","Verify the file path exists and is readable by the Nomad agent if using PemKeyFile"],"exampleFix":"// before\nkey := &api.OIDCClientAssertionKey{KeyID: \"key-1\"}\n// after\nkey := &api.OIDCClientAssertionKey{KeyID: \"key-1\", PemKeyFile: \"/etc/nomad/assertion.key\"}","handlingStrategy":"validation","validationCode":"func assertionKeyOK(k *structs.OIDCClientAssertionKey) error {\n  if k == nil || (k.PemKey == \"\" && k.PemKeyFile == \"\") {\n    return structs.ErrMissingClientAssertionKey\n  }\n  if k.PemKey != \"\" && k.PemKeyFile != \"\" {\n    return structs.ErrAmbiguousClientAssertionKey\n  }\n  return nil\n}","typeGuard":null,"tryCatchPattern":"if err := key.Validate(); err != nil {\n  switch {\n  case errors.Is(err, structs.ErrMissingClientAssertionKey):\n    // set PemKey or PemKeyFile and retry submission\n  case errors.Is(err, structs.ErrAmbiguousClientAssertionKey):\n    // keep only one of PemKey/PemKeyFile\n  }\n}","preventionTips":["Supply exactly one of PemKey/PemKeyFile","Check file existence/permissions when using PemKeyFile","Reuse the library's sentinel errors with errors.Is for branching"],"tags":["nomad","oidc","validation","missing-key"],"backgroundTag":"missing-pem-key","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"}