{"record":{"id":"c79e3f11a49b477e","repo":"hashicorp/nomad","slug":"failed-to-extract-claims-from-token-w","errorCode":null,"errorMessage":"failed to extract claims from token: %w","messagePattern":"failed to extract claims from token: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/node_identity_endpoint.go","lineNumber":53,"sourceCode":"\t// be called before it has a JWT identity. Check we do not get an empty\n\t// string before attempting to parse the token.\n\tif identityToken == \"\" {\n\t\treturn errors.New(\"node does not have a JWT identity token\")\n\t}\n\n\t// Parse the signed JWT token from the node identity and extract the claims\n\t// into a map. This is done to avoid exposing the key material of the signed\n\t// JWT token, but still results in all the claims which is perfect for\n\t// debugging and introspection purposes.\n\tparsedJWT, err := jwt.ParseSigned(identityToken)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parsed signed token: %w\", err)\n\t}\n\n\tclaims := make(map[string]any)\n\n\tif err := parsedJWT.UnsafeClaimsWithoutVerification(&claims); err != nil {\n\t\treturn fmt.Errorf(\"failed to extract claims from token: %w\", err)\n\t}\n\n\tresp.Claims = claims\n\treturn nil\n}\n\nfunc (n *NodeIdentity) Renew(args *structs.NodeIdentityRenewReq, _ *structs.NodeIdentityRenewResp) error {\n\n\t// Check node write permissions.\n\tif aclObj, err := n.c.ResolveToken(args.AuthToken); err != nil {\n\t\treturn err\n\t} else if !aclObj.AllowNodeWrite() {\n\t\treturn structs.ErrPermissionDenied\n\t}\n\n\t// Store the node identity renewal request on the client, so it can be\n\t// picked up at the next heartbeat.\n\tn.c.identityForceRenewal.Store(true)","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/node_identity_endpoint.go#L35-L71","documentation":"This error is returned by node_identity_endpoint.Get when the signed JWT parsed successfully but its claims could not be unmarshaled into a map. It calls UnsafeClaimsWithoutVerification, which decodes the payload without signature verification; failure means the payload JSON cannot be unmarshaled into map[string]any.","triggerScenarios":"Calling the node identity RPC/endpoint when the JWT payload segment is not valid JSON, is not a JSON object (e.g. an array or string), or contains claims that cannot unmarshal into a map.","commonSituations":"A hand-crafted or corrupted token where the payload is not a JSON object; an opaque token issued by a non-Nomad identity provider placed in the token file; truncated base64 payload that decodes to invalid JSON.","solutions":["Regenerate the node identity token by restarting the Nomad client agent so the server issues a fresh, well-formed JWT","Base64-decode the payload segment and confirm it is a JSON object with claim keys","Ensure the token file contains a Nomad-issued identity token, not an arbitrary or foreign token","Check Nomad agent/server version compatibility; older agents should not be fed tokens from newer formats"],"exampleFix":"// before: trusting any token file content\nclaims := make(map[string]any)\nif err := parsedJWT.UnsafeClaimsWithoutVerification(&claims); err != nil {\n    return fmt.Errorf(\"failed to extract claims from token: %w\", err)\n}\n\n// after: validate payload is a JSON object before decoding\npayload, _ := parsedJWT.Payload()\nvar probe map[string]any\nif err := json.Unmarshal(payload, &probe); err != nil {\n    return fmt.Errorf(\"identity token payload is not a JSON object; regenerate node identity token: %w\", err)\n}","handlingStrategy":"validation","validationCode":"payloadB64 := strings.Split(strings.TrimSpace(string(tokenBytes)), \".\")[1]\npayload, err := base64.RawURLEncoding.DecodeString(payloadB64)\nif err != nil {\n\treturn fmt.Errorf(\"identity token payload not base64url: %w\", err)\n}\nvar probe map[string]any\nif err := json.Unmarshal(payload, &probe); err != nil {\n\treturn fmt.Errorf(\"identity token payload is not a JSON object: %w\", err)\n}","typeGuard":"func isJWTObjectPayload(token string) bool {\n\tparts := strings.Split(token, \".\")\n\tif len(parts) != 3 {\n\t\treturn false\n\t}\n\tpayload, err := base64.RawURLEncoding.DecodeString(parts[1])\n\tif err != nil {\n\t\treturn false\n\t}\n\tvar m map[string]any\n\treturn json.Unmarshal(payload, &m) == nil\n}","tryCatchPattern":"claims, err := nodeIdentityEndpoint.Get(ctx)\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to extract claims\") {\n\t\t// token payload not a JSON object: regenerate via agent restart\n\t\treturn regenerateAndRetry(ctx)\n\t}\n\treturn err\n}","preventionTips":["Only consume tokens issued by the Nomad server's identity signing key","Do not substitute foreign/OIDC tokens into the node identity file","Validate token payloads in tooling before persisting them to disk","Upgrade agents/servers together to avoid identity format drift"],"tags":["jwt","nomad-client","claims","json-parsing"],"backgroundTag":"jwt-malformed-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"}