Error message
invalid raw signature length
Also printed as
- invalid raw signature length (argument="signature", value="0x…", code=INVALID_ARGUMENT, version=6.17.0)
- TypeError: invalid raw signature length
`err.shortMessage` is "invalid raw signature length"; `err.message` appends the argument, the value and the ethers version. The error is a `TypeError` with `code: "INVALID_ARGUMENT"`.
ethers v6 (JavaScript) — Signature.from() inside verifyMessage / verifyTypedData / recoverAddress
What it means
ethers tried to interpret the signature bytes as an ECDSA signature for address recovery and the length fits neither form it knows: 65 bytes (r‖s‖v) or 64 bytes (the EIP-2098 compact form). No recovery was attempted. The bytes are usually a different kind of signature altogether — a smart-account signature that has to be checked on-chain, or a DER-encoded signature from a key-management service.
Why it happens
A smart-contract wallet signature
commonSafe, Coinbase Smart Wallet, Argent and other ERC-4337 accounts return signatures that only the account contract can validate (ERC-1271), often wrapped per ERC-6492 for not-yet-deployed accounts. They are hundreds of bytes long and cannot be verified with `verifyMessage`.
A DER signature from AWS KMS or Google Cloud KMS
commonAWS KMS `ECC_SECG_P256K1` and Google Cloud KMS `EC_SIGN_SECP256K1_SHA256` keys return ASN.1 DER (70–72 bytes) without a recovery id. It has to be converted to r‖s‖v first.
Truncated or double-encoded
occasionalA signature cut in a database column, hex of a hex string, or a value that lost its last byte when `v` was stored separately.
How to fix it
- 1.
Verify smart accounts on-chain
Call `isValidSignature` (ERC-1271) on the account, or use a library that implements ERC-6492 for counterfactual accounts (for example viem's `verifyMessage` with a public client).
js import { Contract, hashMessage, JsonRpcProvider } from "ethers"; // Smart accounts (Safe, Coinbase Smart Wallet, ERC-4337 accounts) are verified on-chain (ERC-1271). const provider = new JsonRpcProvider(process.env.RPC_URL); const account = new Contract(address, ["function isValidSignature(bytes32,bytes) view returns (bytes4)"], provider); const magic = await account.isValidSignature(hashMessage(message), signature); const valid = magic === "0x1626ba7e"; - 2.
Convert DER to r‖s‖v
Parse r and s, normalise s to the lower half order (Ethereum rejects high-s values), and try both recovery bits to find the one that yields the expected address.
js import { Signature, recoverAddress, hashMessage, toBeHex } from "ethers"; import { secp256k1 } from "@noble/curves/secp256k1.js"; // @noble/curves v2 const N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n; // secp256k1 order // KMS/HSM: parse DER, move s to the lower half (Ethereum rejects high-s), then find the recovery bit. function derToEthSignature(derBytes, digest, expectedAddress) { const { r, s } = secp256k1.Signature.fromBytes(derBytes, "der"); const lowS = s > N / 2n ? N - s : s; for (const yParity of [0, 1]) { const candidate = Signature.from({ r: toBeHex(r, 32), s: toBeHex(lowS, 32), yParity }); if (recoverAddress(digest, candidate) === expectedAddress) return candidate.serialized; // 65 bytes } throw new Error("signature does not belong to " + expectedAddress); } const signature = derToEthSignature(kmsDer, hashMessage(message), kmsAddress);