{"record":{"id":"d8b56484aba7714b","repo":"hashicorp/nomad","slug":"node-identity-claims-missing-node-pool","errorCode":null,"errorMessage":"node identity claims missing node pool","messagePattern":"node identity claims missing node pool","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/auth/auth.go","lineNumber":747,"sourceCode":"\t\treturn fmt.Errorf(\"allocation does not exist\")\n\t}\n\n\t// the claims for terminal allocs are always treated as expired\n\tif alloc.ClientTerminalStatus() {\n\t\treturn fmt.Errorf(\"allocation is terminal\")\n\t}\n\n\treturn nil\n}\n\nfunc (s *Authenticator) resolveClaims(claims *structs.IdentityClaims) (*acl.ACL, error) {\n\n\t// Nomad node identity claims currently map to a client ACL. If we open this\n\t// up in the future, we will want to modify this section to perform similar\n\t// work that is done for workload claims.\n\tif claims.IsNode() {\n\t\tif claims.NodeIdentityClaims == nil || claims.NodeIdentityClaims.NodePool == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"node identity claims missing node pool\")\n\t\t}\n\t\treturn acl.NewClientACL(claims.NodeIdentityClaims.NodePool), nil\n\t}\n\n\tpolicies, err := s.ResolvePoliciesForClaims(claims)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Compile and cache the ACL object. For many claims this will result in an\n\t// ACL object with no policies, which can be efficiently cached.\n\taclObj, err := structs.CompileACLObject(s.aclCache, policies)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn aclObj, nil\n}\n","sourceCodeStart":729,"sourceCodeEnd":765,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/auth/auth.go#L729-L765","documentation":"resolveClaims maps node identity claims to a client ACL, but a client ACL is scoped to a node pool. If the JWT's NodeIdentityClaims is nil or its NodePool field is empty, the server cannot determine which pool's client capabilities to grant and fails. This is a claim-content validation error in the node identity token.","triggerScenarios":"Presenting a node identity JWT (via ResolveACL or TestResolveClaims) that either lacks NodeIdentityClaims entirely or has an empty NodePool claim — typically a token signed by an older Nomad version or built without the node-pool claim.","commonSituations":"Upgraded cluster where clients still hold identity tokens minted before node-pool claims were added; custom token minters omitting the node_pool claim; misconfigured node identity signing templates.","solutions":["Regenerate the node identity token so it includes a non-empty node_pool claim (upgrade/restart the client on a current Nomad version).","Verify the client's node is assigned to a node pool; ensure the signing template populates NodePool.","Check server and client versions match a release that includes node-pool identity claims."],"exampleFix":"// before (token payload)\n{ \"node_id\": \"abc\", ... } // missing node_pool\n// after\n{ \"node_id\": \"abc\", \"node_pool\": \"prod\", ... }","handlingStrategy":"validation","validationCode":"claims, err := parseJWTUnverified(nodeToken)\nif err != nil { return err }\nif claims[\"node_pool\"] == \"\" || claims[\"node_pool\"] == nil {\n    return fmt.Errorf(\"node token lacks node_pool claim; re-mint token\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep clients and servers on versions that mint node-pool identity claims.","Include node_pool in any custom identity signing templates.","Rotate node identity tokens after cluster upgrades."],"tags":["nomad","node-identity","jwt","node-pool","acl"],"backgroundTag":"missing-required-jwt-claim","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"}