{"record":{"id":"827b1b1e7ac51944","repo":"hashicorp/nomad","slug":"invalid-pemkeyfile","errorCode":null,"errorMessage":"invalid PemKeyFile","messagePattern":"invalid PemKeyFile","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":1842,"sourceCode":"\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 != \"\" {\n\t\treturn ErrAmbiguousClientAssertionKey","sourceCodeStart":1824,"sourceCodeEnd":1860,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L1824-L1860","documentation":"When the client assertion private key is given as a file (PemKeyFile), the path must be absolute. ErrInvalidClientAssertionKeyPath is wrapped (fmt.Errorf %w) with 'must be absolute; got: <path>' when a relative path is supplied, because the key file is read by agents whose working directory is not the operator's.","triggerScenarios":"Validate() where k.PemKeyFile != \"\" and path.IsAbs(k.PemKeyFile) is false, e.g. \"tls/client.key\" or \"./client.key\".","commonSituations":"Configs written relative to a deploy directory; paths copied from a local dev shell; templating that emits ~ or relative refs; agents running under systemd with different WorkingDirectory.","solutions":["Change PemKeyFile to an absolute path like /etc/nomad/tls/client.key","Expand ~ and relative segments in your config tooling before applying","Verify with path.IsAbs in scripts that generate the config"],"exampleFix":"// before\nkey := &structs.OIDCClientAssertionKey{\n  PemKeyFile: \"./tls/client.key\",\n}\n// after\nkey := &structs.OIDCClientAssertionKey{\n  PemKeyFile: \"/etc/nomad/tls/client.key\",\n}","handlingStrategy":"validation","validationCode":"if key.PemKeyFile != \"\" && !path.IsAbs(key.PemKeyFile) {\n    abs, err := filepath.Abs(key.PemKeyFile)\n    if err != nil { return err }\n    key.PemKeyFile = abs\n}","typeGuard":null,"tryCatchPattern":"if err := key.Validate(); err != nil {\n    var wrapped error\n    if errors.As(err, &wrapped) && strings.Contains(err.Error(), ErrInvalidClientAssertionKeyPath.Error()) {\n        // convert to an absolute path and retry validation\n    }\n    return err\n}","preventionTips":["Use absolute paths for all key/cert files in Nomad config","Remember the agent's CWD differs from your shell (systemd, containers)","Run filepath.Abs in config generation scripts"],"tags":["nomad","oidc","client-assertion","file-path"],"backgroundTag":"relative-path-not-allowed","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"}