hashicorp/nomad · error
cannot specify Accessor ID
Error message
cannot specify Accessor ID
What it means
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.
Source
Thrown at api/acl.go:133
}
return &resp, wm, nil
}
// List is used to dump all of the tokens.
func (a *ACLTokens) List(q *QueryOptions) ([]*ACLTokenListStub, *QueryMeta, error) {
var resp []*ACLTokenListStub
qm, err := a.client.query("/v1/acl/tokens", &resp, q)
if err != nil {
return nil, nil, err
}
return resp, qm, nil
}
// Create is used to create a token with server-generated AccessorID and
// SecretID. Use Upload to create a token with pre-specified IDs.
func (a *ACLTokens) Create(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
if token.AccessorID != "" {
return nil, nil, errors.New("cannot specify Accessor ID")
}
var resp ACLToken
wm, err := a.client.put("/v1/acl/token", token, &resp, q)
if err != nil {
return nil, nil, err
}
return &resp, wm, nil
}
// Upload is used to create a client token with pre-specified AccessorID and
// SecretID. Management tokens cannot be uploaded and must be created with Create.
func (a *ACLTokens) Upload(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
if token.AccessorID == "" {
return nil, nil, errors.New("missing accessor ID")
}
if token.SecretID == "" {
return nil, nil, errors.New("missing secret ID")
}View on GitHub (pinned to 482b49bf1a)
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
Example fix
// before
if err.Error() == "node not found abc123" { retry() }
// after
if errors.Is(err, structs.ErrUnknownNode) {
// node permanently gone: release claim instead of retrying
claim.State = structs.CSIVolumeClaimStateReadyToFree
} Defensive patterns
Strategy: try-catch
Validate before calling
node, _, err := client.Nodes().Info(nodeID, nil)
if err != nil || node == nil { /* treat as unknown node: skip detach, free claim */ } Try / catch
if errors.Is(err, structs.ErrUnknownNode) {
// node permanently gone — don't retry, mark claim ReadyToFree
claim.State = structs.CSIVolumeClaimStateReadyToFree
} Prevention
- Use errors.Is against ErrUnknownNode, never string matching
- Release claims before node GC/purge
- Track node lifecycle (drain -> release volumes -> purge) in automation
When it happens
Trigger: Publish/unpublish of a volume claim whose NodeID references a deregistered, GC'd, or purged Nomad client; stale claims surviving node removal.
Common situations: 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).
Related errors
- error parsing: root should be an object
- failed to read dynamic plugin registry state: %v
- error getting plugin: %s, %v
- error querying volume %q: %v
- error querying plugin %q: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/fcad94a4f0a3fd08.
Report an issue: GitHub.