{"record":{"id":"fd410437e7fed447","repo":"hashicorp/nomad","slug":"acl-token-lookup-failed-missing-accessor-id","errorCode":null,"errorMessage":"acl token lookup failed: missing accessor id","messagePattern":"acl token lookup failed: missing accessor id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/state/state_store.go","lineNumber":6436,"sourceCode":"\ttxn := s.db.WriteTxnMsgT(msgType, index)\n\tdefer txn.Abort()\n\n\t// Delete the tokens\n\tfor _, id := range ids {\n\t\tif _, err := txn.DeleteAll(\"acl_token\", \"id\", id); err != nil {\n\t\t\treturn fmt.Errorf(\"deleting acl token failed: %v\", err)\n\t\t}\n\t}\n\tif err := txn.Insert(\"index\", &IndexEntry{\"acl_token\", index}); err != nil {\n\t\treturn fmt.Errorf(\"index update failed: %v\", err)\n\t}\n\treturn txn.Commit()\n}\n\n// ACLTokenByAccessorID is used to lookup a token by accessor ID\nfunc (s *StateStore) ACLTokenByAccessorID(ws memdb.WatchSet, id string) (*structs.ACLToken, error) {\n\tif id == \"\" {\n\t\treturn nil, fmt.Errorf(\"acl token lookup failed: missing accessor id\")\n\t}\n\n\ttxn := s.db.ReadTxn()\n\n\twatchCh, existing, err := txn.FirstWatch(\"acl_token\", \"id\", id)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"acl token lookup failed: %v\", err)\n\t}\n\tws.Add(watchCh)\n\n\t// If the existing token is nil, this indicates it does not exist in state.\n\tif existing == nil {\n\t\treturn nil, nil\n\t}\n\n\t// Assert the token type which allows us to perform additional work on the\n\t// token that is needed before returning the call.\n\ttoken := existing.(*structs.ACLToken)","sourceCodeStart":6418,"sourceCodeEnd":6454,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/state/state_store.go#L6418-L6454","documentation":"A validation error from StateStore.ACLTokenByAccessorID: the caller passed an empty accessor ID string. The store refuses the lookup up-front rather than doing a pointless table scan that would match nothing. It is a caller-input bug, not a state problem.","triggerScenarios":"Calling ACLTokenByAccessorID with \"\" — e.g., resolving a token from an HTTP request whose X-Nomad-Token header is empty, or code that did not check a struct field before lookup.","commonSituations":"Clients sending requests without a token header; config where a token variable was never set; fuzzer/test callers omitting the ID; handlers that strip empty strings after trimming whitespace.","solutions":["Check that the accessor ID is non-empty before calling the store (strings.TrimSpace + len check).","At the API layer, reject requests with a missing token early with a 400/permission-denied instead of reaching the state store.","Audit call sites so empty IDs short-circuit with a domain error rather than the store sentinel."],"exampleFix":"// before\ntoken, err := store.ACLTokenByAccessorID(ws, req.SecretToken)\n// after\nif req.SecretToken == \"\" {\n    return structs.NewErrRPCCoded(400, \"missing token secret ID\")\n}\ntoken, err := store.ACLTokenByAccessorID(ws, req.SecretToken)","handlingStrategy":"validation","validationCode":"func canLookupByAccessor(id string) bool {\n    return strings.TrimSpace(id) != \"\"\n}\n// before calling:\n// if !canLookupByAccessor(accessorID) { return errors.New(\"missing accessor id\") }","typeGuard":"func hasAccessorID(t *struct.ACLToken) bool {\n    return t != nil && t.AccessorID != \"\"\n}","tryCatchPattern":"if accessorID == \"\" {\n    return fmt.Errorf(\"acl token lookup skipped: accessor id required\")\n}\ntoken, err := store.ACLTokenByAccessorID(ws, accessorID)\nif err != nil { return err }","preventionTips":["Always check token strings for emptiness (and whitespace) at the API edge","Reject token-less requests early with 400/403 instead of hitting the store","Trim X-Nomad-Token header values before lookup","Add tests covering the empty-ID path of your handlers"],"tags":["nomad","state-store","acl","input-validation"],"backgroundTag":"missing-id-token","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"}