{"record":{"id":"db69ee955381785e","repo":"hashicorp/nomad","slug":"missing-pemcert-pemcertfile-or-keyid","errorCode":null,"errorMessage":"missing PemCert, PemCertFile, or KeyID","messagePattern":"missing PemCert, PemCertFile, or KeyID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":1840,"sourceCode":"\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\n\t}","sourceCodeStart":1822,"sourceCodeEnd":1858,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L1822-L1858","documentation":"An OIDC client assertion needs a way to identify the signing key: either an inline certificate (PemCert), a certificate file (PemCertFile), or a precomputed key ID (KeyID). ErrMissingClientAssertionKeyID is returned when none of the three is provided, so the JWT 'kid'/x5t header could not be derived.","triggerScenarios":"Validate() on an OIDCClientAssertionKey where PemCert == \"\" && PemCertFile == \"\" && KeyID == \"\".","commonSituations":"Operator configured only the private key (PemKey/PemKeyFile) but forgot the matching certificate or key ID; partial migration of auth-method config; copying a key-only example.","solutions":["Set PemCertFile to the absolute path of the certificate matching the private key","Or set PemCert to the inline PEM certificate","Or set KeyID plus KeyIDHeader (KeyID requires KeyIDHeader \"kid\")"],"exampleFix":"// before\nkey := &structs.OIDCClientAssertionKey{\n  PemKeyFile: \"/etc/nomad/tls/client.key\",\n}\n// after\nkey := &structs.OIDCClientAssertionKey{\n  PemKeyFile: \"/etc/nomad/tls/client.key\",\n  PemCertFile: \"/etc/nomad/tls/client.crt\",\n}","handlingStrategy":"validation","validationCode":"if key.PemCert == \"\" && key.PemCertFile == \"\" && key.KeyID == \"\" {\n    return fmt.Errorf(\"client assertion needs PemCert, PemCertFile, or KeyID\")\n}\nif err := key.Validate(); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := key.Validate(); err != nil {\n    if errors.Is(err, structs.ErrMissingClientAssertionKeyID) {\n        // add a cert or KeyID before applying\n    }\n    return err\n}","preventionTips":["Always pair the private key with its certificate or a known KeyID","Document that PemKey alone is insufficient","Validate auth-method configs in CI before nomad agent applies them"],"tags":["nomad","oidc","client-assertion","config-validation"],"backgroundTag":"missing-required-argument","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"}