{"record":{"id":"5224e778a185446d","repo":"hashicorp/nomad","slug":"require-only-one-of-pemcert-pemcertfile-or-keyid","errorCode":null,"errorMessage":"require only one of PemCert, PemCertFile, or KeyID","messagePattern":"require only one of PemCert, PemCertFile, or KeyID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":1841,"sourceCode":"func (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}\n\tif k.PemKey != \"\" && k.PemKeyFile != \"\" {","sourceCodeStart":1823,"sourceCodeEnd":1859,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L1823-L1859","documentation":"Exactly one key-identification mechanism must be chosen for a client assertion. ErrAmbiguousClientAssertionKeyID (ErrMissingClientAssertionKeyID's sibling) is returned when more than one of PemCert, PemCertFile, or KeyID is set, since Nomad cannot decide which identifier to put in the JWT header.","triggerScenarios":"Validate() where PemCert is set together with PemCertFile or KeyID; or PemCertFile set together with PemCert or KeyID; or KeyID set together with PemCert or PemCertFile.","commonSituations":"Operator supplies the full chain in a file but also pastes a leaf cert inline; automation computes KeyID while the config still carries PemCertFile; gradual refactors leaving stale fields populated.","solutions":["Keep only one identifier: prefer PemCertFile for file-based setups","If using a precomputed KeyID, clear PemCert and PemCertFile and set KeyIDHeader appropriately","If using inline cert, clear PemCertFile and KeyID"],"exampleFix":"// before\nkey := &structs.OIDCClientAssertionKey{\n  PemCertFile: \"/etc/nomad/tls/client.crt\",\n  KeyID: \"abc123\",\n  KeyIDHeader: structs.OIDCClientAssertionHeaderKid,\n}\n// after\nkey := &structs.OIDCClientAssertionKey{\n  KeyID: \"abc123\",\n  KeyIDHeader: structs.OIDCClientAssertionHeaderKid,\n}","handlingStrategy":"validation","validationCode":"set := 0\nfor _, v := range []string{key.PemCert, key.PemCertFile, key.KeyID} {\n    if v != \"\" { set++ }\n}\nif set > 1 { return errors.New(\"choose exactly one of PemCert, PemCertFile, KeyID\") }","typeGuard":null,"tryCatchPattern":"if err := key.Validate(); err != nil {\n    if errors.Is(err, structs.ErrAmbiguousClientAssertionKeyID) {\n        // strip the redundant identifier fields\n    }\n    return err\n}","preventionTips":["Pick one identification style per auth method and stick to it","Clear removed fields when refactoring config (no stale leftovers)","Prefer PemCertFile (abs path) or KeyID+kid; avoid inline certs"],"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"}