hashicorp/nomad · error

failed to parsed signed token: %w

Error message

failed to parsed signed token: %w

What it means

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.

Source

Thrown at client/node_identity_endpoint.go:47

		return structs.ErrPermissionDenied
	}

	identityToken := n.c.nodeIdentityToken()

	// The client could be upgraded before all the servers allowing this API to
	// be called before it has a JWT identity. Check we do not get an empty
	// string before attempting to parse the token.
	if identityToken == "" {
		return errors.New("node does not have a JWT identity token")
	}

	// Parse the signed JWT token from the node identity and extract the claims
	// into a map. This is done to avoid exposing the key material of the signed
	// JWT token, but still results in all the claims which is perfect for
	// debugging and introspection purposes.
	parsedJWT, err := jwt.ParseSigned(identityToken)
	if err != nil {
		return fmt.Errorf("failed to parsed signed token: %w", err)
	}

	claims := make(map[string]any)

	if err := parsedJWT.UnsafeClaimsWithoutVerification(&claims); err != nil {
		return fmt.Errorf("failed to extract claims from token: %w", err)
	}

	resp.Claims = claims
	return nil
}

func (n *NodeIdentity) Renew(args *structs.NodeIdentityRenewReq, _ *structs.NodeIdentityRenewResp) error {

	// Check node write permissions.
	if aclObj, err := n.c.ResolveToken(args.AuthToken); err != nil {
		return err
	} else if !aclObj.AllowNodeWrite() {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Inspect the node identity token file and confirm it is a three-segment dot-separated JWT (header.payload.signature)
  2. Delete the corrupted/empty token file and restart the Nomad agent so it regenerates a fresh signed token
  3. Verify file permissions and that no external process truncates the token file while the agent is running
  4. Check agent logs for the underlying go-jose parse error to confirm whether the token is malformed vs merely unverifiable

Example fix

// before: reading a stale/empty token file directly
identityToken := string(badTokenBytes)
parsedJWT, err := jwt.ParseSigned(identityToken)

// after: guard for empty/malformed token before parsing
identityToken := strings.TrimSpace(string(badTokenBytes))
if identityToken == "" || strings.Count(identityToken, ".") != 2 {
    return fmt.Errorf("node identity token missing or malformed; restart agent to regenerate")
}
parsedJWT, err := jwt.ParseSigned(identityToken)
Defensive patterns

Strategy: validation

Validate before calling

token := strings.TrimSpace(string(tokenBytes))
if token == "" || strings.Count(token, ".") != 2 {
    return fmt.Errorf("node identity token missing or not a JWT compact token")
}
if _, err := base64.RawURLEncoding.DecodeString(strings.Split(token, ".")[0]); err != nil {
    return fmt.Errorf("identity token header segment not valid base64url: %w", err)
}

Type guard

func isCompactJWT(s string) bool {
	parts := strings.Split(s, ".")
	if len(parts) != 3 {
		return false
	}
	for _, p := range parts {
		if _, err := base64.RawURLEncoding.DecodeString(p); err != nil {
			return false
		}
	}
	return true
}

Try / catch

claims, err := nodeIdentityEndpoint.Get(ctx)
if err != nil {
	if strings.Contains(err.Error(), "failed to parsed signed token") {
		// regenerate token: restart agent or re-fetch identity
		return refreshIdentityToken(ctx)
	}
	return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/844c6e1bc2b4f6c5. Report an issue: GitHub.