{"record":{"id":"0bb58eb14e0c308d","repo":"hashicorp/nomad","slug":"acl-token-not-found","errorCode":null,"errorMessage":"ACL token not found","messagePattern":"ACL token not found","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/errors.go","lineNumber":56,"sourceCode":"\n\terrRPCCodedErrorPrefix = \"RPC Error:: \"\n\n\terrDeploymentTerminalNoCancel    = \"can't cancel terminal deployment\"\n\terrDeploymentTerminalNoFail      = \"can't fail terminal deployment\"\n\terrDeploymentTerminalNoPause     = \"can't pause terminal deployment\"\n\terrDeploymentTerminalNoPromote   = \"can't promote terminal deployment\"\n\terrDeploymentTerminalNoResume    = \"can't resume terminal deployment\"\n\terrDeploymentTerminalNoUnblock   = \"can't unblock terminal deployment\"\n\terrDeploymentTerminalNoRun       = \"can't run terminal deployment\"\n\terrDeploymentTerminalNoSetHealth = \"can't set health of allocations for a terminal deployment\"\n\terrDeploymentRunningNoUnblock    = \"can't unblock running deployment\"\n)\n\nvar (\n\tErrNoLeader                   = errors.New(errNoLeader)\n\tErrNotReadyForConsistentReads = errors.New(errNotReadyForConsistentReads)\n\tErrNoRegionPath               = errors.New(errNoRegionPath)\n\tErrTokenNotFound              = errors.New(errTokenNotFound)\n\tErrTokenExpired               = errors.New(errTokenExpired)\n\tErrTokenInvalid               = errors.New(errTokenInvalid)\n\tErrPermissionDenied           = errors.New(errPermissionDenied)\n\tErrJobRegistrationDisabled    = errors.New(errJobRegistrationDisabled)\n\tErrNoNodeConn                 = errors.New(errNoNodeConn)\n\tErrUnknownMethod              = errors.New(errUnknownMethod)\n\tErrUnknownNomadVersion        = errors.New(errUnknownNomadVersion)\n\tErrNodeLacksRpc               = errors.New(errNodeLacksRpc)\n\tErrMissingAllocID             = errors.New(errMissingAllocID)\n\tErrIncompatibleFiltering      = errors.New(errIncompatibleFiltering)\n\tErrMalformedChooseParameter   = errors.New(errMalformedChooseParameter)\n\n\t// ErrResultPaginatorCreation is returned by list RPC handlers when the\n\t// result paginator cannot be built, for example when the server cannot\n\t// evaluate a requested filter expression. api.ResultPaginatorErrorContent\n\t// duplicates its message so the CLI can match it without importing structs.\n\t// Keep the two in sync.\n\tErrResultPaginatorCreation = errors.New(errResultPaginatorCreation)","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/errors.go#L38-L74","documentation":"ErrTokenNotFound is a sentinel (nomad/structs/errors.go:56) returned when an ACL token (or equivalent credential) cannot be resolved for an RPC. It surfaces from token/role resolution paths (resolveTokenAndACL, ACL role handlers) and from the client ACL layer (client/acl.go:82) when a workload identity carries neither an ACLToken nor claims. The HTTP API maps it to a 403 response (command/agent/http.go:762).","triggerScenarios":"Passing an `X-Nomad-Token` whose SecretID doesn't exist (expired/deleted token); empty or missing token where ACLs are enabled; client RPC with a workload identity lacking both ACLToken and Claims; referencing a token that was revoked or purged from state store.","commonSituations":"Stale NOMAD_TOKEN env var or CLI config after token rotation; CI/CD using a token deleted by policy; upgrading workloads to workload identities while the old ACL token was revoked; copy-pasting a truncated secret ID.","solutions":["Generate a new token with `nomad acl token create` (or via SSO/login for workload identities) and update the client's credentials","Verify the token value is complete and correctly set (NOMAD_TOKEN env, -token flag, or agent config) — check for truncation or whitespace","If using workload identities, ensure the job/task identity is configured so Claims are present, or that the ACL token was injected into the alloc","Check `nomad acl token list` (with a management token) to confirm the token still exists and is not expired"],"exampleFix":"// before\nclient.SetToken(os.Getenv(\"NOMAD_TOKEN\")) // stale/revoked token\n// after\ntoken := os.Getenv(\"NOMAD_TOKEN\")\nif token == \"\" {\n    token = fetchNewTokenFromVault() // re-issue from secure store\n}\nclient.SetToken(token)","handlingStrategy":"try-catch","validationCode":"// verify the token resolves before making calls\n_, _, err := client.ACL().GetToken(tokenID)\nif err != nil {\n    return fmt.Errorf(\"token %q is invalid or deleted; re-issue credentials\", tokenID)\n}","typeGuard":"func isTokenNotFound(err error) bool {\n    return errors.Is(err, structs.ErrTokenNotFound) || strings.Contains(err.Error(), structs.ErrTokenNotFound.Error())\n}","tryCatchPattern":"resp, err := client.System().ListMembers()\nif err != nil {\n    if isTokenNotFound(err) {\n        // HTTP 403 path: refresh credentials and retry once\n        client.SetToken(rotateToken())\n        resp, err = client.System().ListMembers()\n    }\n    if err != nil {\n        return err\n    }\n}","preventionTips":["Store tokens in a secret manager and refresh them before expiry rather than hardcoding","Handle 403 with token-not-found distinctly from permission-denied: one means re-auth, the means re-authz","For workload identities, ensure jobs declare identities so Claims are attached to client RPCs","Audit token lifetimes (TTLs) and automate renewal/rotation in CI/CD pipelines"],"tags":["nomad","acl","authentication","token","security"],"backgroundTag":"acl-token-not-found","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"}