{"record":{"id":"844c6e1bc2b4f6c5","repo":"hashicorp/nomad","slug":"failed-to-parsed-signed-token-w","errorCode":null,"errorMessage":"failed to parsed signed token: %w","messagePattern":"failed to parsed signed token: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/node_identity_endpoint.go","lineNumber":47,"sourceCode":"\t\treturn structs.ErrPermissionDenied\n\t}\n\n\tidentityToken := n.c.nodeIdentityToken()\n\n\t// The client could be upgraded before all the servers allowing this API to\n\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() {","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/node_identity_endpoint.go#L29-L65","documentation":"This error is returned by node_identity_endpoint.Get when parsing the client's signed JWT identity token fails. The endpoint parses the JWT solely to expose its claims for debugging/introspection, without verifying the signature. If the token bytes are not a well-formed JWS compact serialization, go-jose's jwt.ParseSigned fails and the underlying error is wrapped here.","triggerScenarios":"Calling the Node identity RPC (Client.NodeIdentity or the agent's /v1/node/identity HTTP endpoint) when the identity token file on disk is empty, truncated, corrupted, or not in JWT compact form.","commonSituations":"A leftover or partially written node_identity_token file from a failed startup; manual edits to the token file; Nomad versions where the token file is missing content; disk corruption or truncated file after crash; mounting an empty secret/configMap path over the token file.","solutions":["Inspect the node identity token file and confirm it is a three-segment dot-separated JWT (header.payload.signature)","Delete the corrupted/empty token file and restart the Nomad agent so it regenerates a fresh signed token","Verify file permissions and that no external process truncates the token file while the agent is running","Check agent logs for the underlying go-jose parse error to confirm whether the token is malformed vs merely unverifiable"],"exampleFix":"// before: reading a stale/empty token file directly\nidentityToken := string(badTokenBytes)\nparsedJWT, err := jwt.ParseSigned(identityToken)\n\n// after: guard for empty/malformed token before parsing\nidentityToken := strings.TrimSpace(string(badTokenBytes))\nif identityToken == \"\" || strings.Count(identityToken, \".\") != 2 {\n    return fmt.Errorf(\"node identity token missing or malformed; restart agent to regenerate\")\n}\nparsedJWT, err := jwt.ParseSigned(identityToken)","handlingStrategy":"validation","validationCode":"token := strings.TrimSpace(string(tokenBytes))\nif token == \"\" || strings.Count(token, \".\") != 2 {\n    return fmt.Errorf(\"node identity token missing or not a JWT compact token\")\n}\nif _, err := base64.RawURLEncoding.DecodeString(strings.Split(token, \".\")[0]); err != nil {\n    return fmt.Errorf(\"identity token header segment not valid base64url: %w\", err)\n}","typeGuard":"func isCompactJWT(s string) bool {\n\tparts := strings.Split(s, \".\")\n\tif len(parts) != 3 {\n\t\treturn false\n\t}\n\tfor _, p := range parts {\n\t\tif _, err := base64.RawURLEncoding.DecodeString(p); err != nil {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","tryCatchPattern":"claims, err := nodeIdentityEndpoint.Get(ctx)\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to parsed signed token\") {\n\t\t// regenerate token: restart agent or re-fetch identity\n\t\treturn refreshIdentityToken(ctx)\n\t}\n\treturn err\n}","preventionTips":["Never hand-edit or truncate the node identity token file","Ensure the token file is fully written before clients read it (use atomic file writes)","Restart the Nomad client agent to regenerate tokens after corruption","Monitor for zero-byte token files in the client data/secret dir"],"tags":["jwt","nomad-client","token-parsing","node-identity"],"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"}