hashicorp/nomad · error

invalid claims: %w

Error message

invalid claims: %w

What it means

The JWT signature was valid, but claims.Validate(jwt.Expected{}) rejected the token — e.g. the claims payload could not unmarshal into structs.IdentityClaims or contains fields failing jose validation. COMPAT note: with empty Expected{}, most time-based checks are off, so this mostly indicates a malformed or unexpected claims body.

Source

Thrown at nomad/encrypter.go:393

	}

	typedPubKey, err := pubKey.GetPublicKey()
	if err != nil {
		return nil, err
	}

	claims := structs.IdentityClaims{}

	// Validate the claims.
	if err := token.Claims(typedPubKey, &claims); err != nil {
		return nil, fmt.Errorf("invalid signature: %w", err)
	}

	// COMPAT: Until we can guarantee there are no pre-1.7 JWTs in use, we can
	// only validate the signature and have no further expectations of the
	// claims.
	if err := claims.Validate(jwt.Expected{}); err != nil {
		return nil, fmt.Errorf("invalid claims: %w", err)
	}

	return &claims, nil
}

// AddUnwrappedKey stores the key in the keystore and creates a new cipher for
// it. This is called in the RPC handlers on the leader and from the legacy
// KeyringReplicator.
func (e *Encrypter) AddUnwrappedKey(rootKey *structs.UnwrappedRootKey, isUpgraded bool) (*structs.RootKey, error) {

	// note: we don't lock the keyring here but inside addCipher
	// instead, so that we're not holding the lock while performing
	// local disk writes
	if err := e.addCipher(rootKey); err != nil {
		return nil, err
	}
	return e.wrapRootKey(rootKey, isUpgraded)
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Re-issue the token from the current Nomad server so the claims match structs.IdentityClaims
  2. Confirm issuer and verifier run compatible Nomad versions
  3. Decode the token payload (base64url of the middle segment) and inspect for unexpected fields
  4. Ensure the token wasn't modified between issuance and verification

Example fix

// before: token from Nomad 1.6 verified by 1.7+ server
claims, err := e.VerifyClaim(pre17Token) // invalid claims
// after: upgrade the issuing server and mint a fresh token
$ nomad agent -server -version=1.7.x  # then re-run the workload
Defensive patterns

Strategy: validation

Validate before calling

// decode payload and confirm expected claim fields before VerifyClaim
import "encoding/base64"
import "encoding/json"
func hasExpectedClaimShape(token string) bool {
  parts := strings.Split(token, ".")
  if len(parts) != 3 { return false }
  raw, err := base64.RawURLEncoding.DecodeString(parts[1])
  if err != nil { return false }
  var m map[string]any
  if json.Unmarshal(raw, &m) != nil { return false }
  _, hasSub := m["sub"]
  return hasSub // adjust to IdentityClaims requirements
}

Try / catch

claims, err := encrypter.VerifyClaim(token)
if err != nil && strings.Contains(err.Error(), "invalid claims") {
  return fmt.Errorf("token claims incompatible with this server version — re-issue from a current Nomad server: %w", err)
}

Prevention

When it happens

Trigger: VerifyClaim calls claims.Validate(jwt.Expected{}) after successful signature validation; a claims set that doesn't decode into IdentityClaims or violates expected registered claims triggers the wrap.

Common situations: Tokens minted by a much older Nomad version (pre-1.7 claim shape) or by a third-party issuer, hand-forged tokens with an unexpected payload, or corruption in transit that survived the base64 check but broke JSON decoding.

Related errors


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