{"record":{"id":"fcad94a4f0a3fd08","repo":"hashicorp/nomad","slug":"cannot-specify-accessor-id","errorCode":null,"errorMessage":"cannot specify Accessor ID","messagePattern":"cannot specify Accessor ID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/acl.go","lineNumber":133,"sourceCode":"\t}\n\treturn &resp, wm, nil\n}\n\n// List is used to dump all of the tokens.\nfunc (a *ACLTokens) List(q *QueryOptions) ([]*ACLTokenListStub, *QueryMeta, error) {\n\tvar resp []*ACLTokenListStub\n\tqm, err := a.client.query(\"/v1/acl/tokens\", &resp, q)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn resp, qm, nil\n}\n\n// Create is used to create a token with server-generated AccessorID and\n// SecretID. Use Upload to create a token with pre-specified IDs.\nfunc (a *ACLTokens) Create(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {\n\tif token.AccessorID != \"\" {\n\t\treturn nil, nil, errors.New(\"cannot specify Accessor ID\")\n\t}\n\tvar resp ACLToken\n\twm, err := a.client.put(\"/v1/acl/token\", token, &resp, q)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn &resp, wm, nil\n}\n\n// Upload is used to create a client token with pre-specified AccessorID and\n// SecretID. Management tokens cannot be uploaded and must be created with Create.\nfunc (a *ACLTokens) Upload(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {\n\tif token.AccessorID == \"\" {\n\t\treturn nil, nil, errors.New(\"missing accessor ID\")\n\t}\n\tif token.SecretID == \"\" {\n\t\treturn nil, nil, errors.New(\"missing secret ID\")\n\t}","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/api/acl.go#L115-L151","documentation":"lookupExternalNodeID could not find a node in the Nomad state store matching claim.NodeID and returns ErrUnknownNode (wrapped via %w so errors.Is works). Callers (controllerPublishVolume/controllerUnpublishVolume) treat this specially — unpublish skips detach and marks the claim ReadyToFree.","triggerScenarios":"Publish/unpublish of a volume claim whose NodeID references a deregistered, GC'd, or purged Nomad client; stale claims surviving node removal.","commonSituations":"Node was drained and garbage collected with an active claim; cluster state restored from backup without the node; client ID changed after re-provisioning (autoscaled nodes).","solutions":["Check nomad node status to see if the node re-registered under a new ID","Update the claim/volume to reference the current node ID, or re-run the workload so a fresh claim is created","For unpublish, no action needed — Nomad marks claim ReadyToFree and frees it","Match with errors.Is(err, structs.ErrUnknownNode) in custom tooling rather than string compare"],"exampleFix":"// before\nif err.Error() == \"node not found abc123\" { retry() }\n// after\nif errors.Is(err, structs.ErrUnknownNode) {\n    // node permanently gone: release claim instead of retrying\n    claim.State = structs.CSIVolumeClaimStateReadyToFree\n}","handlingStrategy":"try-catch","validationCode":"node, _, err := client.Nodes().Info(nodeID, nil)\nif err != nil || node == nil { /* treat as unknown node: skip detach, free claim */ }","typeGuard":null,"tryCatchPattern":"if errors.Is(err, structs.ErrUnknownNode) {\n    // node permanently gone — don't retry, mark claim ReadyToFree\n    claim.State = structs.CSIVolumeClaimStateReadyToFree\n}","preventionTips":["Use errors.Is against ErrUnknownNode, never string matching","Release claims before node GC/purge","Track node lifecycle (drain -> release volumes -> purge) in automation"],"tags":["nomad","csi","unknown-node","state-store"],"backgroundTag":"unknown-csi-node","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"}