Error message
Invalid primary type `Email` must be one of `["EIP712Domain","Mail"]`.
The names in backticks are your `primaryType` and the available types. viem 2.57 throws it with `err.name` "BaseError"; `err.shortMessage` is the text above and `err.message` adds a hint, a docs link ("typeddatainvalidprimarytypeerror") and the viem version.
viem (TypeScript) — hashTypedData / signTypedData / verifyTypedData / recoverTypedDataAddress
What it means
The `primaryType` you passed is not a key of `types` (viem adds `EIP712Domain` to the list it shows). EIP-712 hashes the message as an instance of the primary type, so viem refuses to guess. The typed data itself may be fine; the name of the struct being signed does not exist in it.
Why it happens
The struct was renamed on one side
commonThe contract or back end renamed `Mail` to `Email` (or `Order` to `OrderV2`), and the front end still sends the old name — or the reverse. Since the type name is part of the type hash, both sides must change together anyway.
primaryType omitted or undefined
occasionalPayloads built dynamically (for example from JSON) where `primaryType` was never set produce `Invalid primary type \`undefined\``.
Different capitalisation
occasional`mail` vs `Mail`: type names are case-sensitive in EIP-712.
How to fix it
- 1.
Declare types as const
With `as const`, viem's types make `primaryType` a union of your struct names, so a mismatch fails at compile time instead of at signing time.
ts const types = { Mail: [ { name: "to", type: "address" }, { name: "contents", type: "string" }, ], } as const; // "as const" lets TypeScript reject a wrong primaryType at compile time const signature = await walletClient.signTypedData({ account, domain, types, primaryType: "Mail", message });