linera-io/linera-protocol · error · Error
MetaMask signature request failed: ${err?.message || err}
Error message
MetaMask signature request failed: ${err?.message || err} What it means
sign() catches every failure of the personal_sign round-trip and rethrows it with the underlying message appended. The most common underlying cause is the user rejecting the prompt (EIP-1193 error code 4001), but it also covers unsupported methods, internal wallet errors, and network problems — the original err.code is lost to the caller by this wrap.
Source
Thrown at web/@linera/metamask/src/signer.ts:76
`MetaMask is not connected with the requested owner: ${owner}`,
);
}
// Encode message as hex string
const msgHex = `0x${uint8ArrayToHex(value)}`;
try {
const signature = (await window.ethereum.request({
method: "personal_sign",
params: [msgHex, owner],
})) as string;
if (!signature) {
throw new Error("No signature returned");
}
return signature;
} catch (err: any) {
throw new Error(
`MetaMask signature request failed: ${err?.message || err}`,
);
}
}
async getPublicKey(_owner: string): Promise<string> {
// MetaMask signs only for `Address20` (EVM secp256k1) owners. The wasm bridge
// never calls `getPublicKey` on the Address20 path — EVM signatures carry the
// signer's address inline in `AccountSignature::EvmSecp256k1`. If we get here,
// a caller is reaching past the bridge contract.
throw new Error(
"MetaMask signer does not expose a public key; EVM signatures carry the address",
);
}
async containsKey(owner: string): Promise<boolean> {
const accounts = await this.provider.send("eth_requestAccounts", []);
return accounts.some(View on GitHub (pinned to 6c226ddcb3)
Solutions
- Parse the wrapped message or, better, detect rejection before wrapping: treat messages containing 'user rejected' / code 4001 as a benign cancel and prompt again with a clear explanation.
- Fire the sign request from a user gesture with context so users accept it.
- If rejection is expected flow, catch this error and degrade gracefully instead of surfacing a stack trace.
Example fix
// before
throw new Error(`MetaMask signature request failed: ${err?.message || err}`); // code lost
// after
if (err?.code === 4001) throw new Error('User rejected the signature request');
throw new Error(`MetaMask signature request failed: ${err?.message || err}`); Defensive patterns
Strategy: try-catch
Try / catch
try { const sig = await signer.sign(owner, bytes); }
catch (e) {
const msg = e instanceof Error ? e.message : String(e);
if (msg.includes('user rejected') || msg.includes('4001')) { /* benign cancel: re-prompt with context */ }
else { /* surface wallet/network failure */ }
} Prevention
- Always sign from a user gesture with a clear explanation of what is being signed.
- Check err.code === 4001 before wrapping provider errors so rejection stays distinguishable.
- Do not auto-retry rejections — only retry infrastructure failures.
When it happens
Trigger: User clicks 'Reject' in the MetaMask prompt; the wallet throws -32601 (method not found) for personal_sign; provider disconnects mid-request; any throw from window.ethereum.request inside the try block (including the hex-encoding step).
Common situations: Users cancelling signing because the prompt surprised them (request fired without a user gesture); wallet locked or in a bad state; dApps displaying the raw wrapped message instead of a friendly 'request denied' notice.
Related errors
- No signature returned
- MetaMask signer does not expose a public key; EVM signatures
- The received chain info response is invalid
- UnauthenticatedTransferOwner
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/b2574216eca751ae.
Report an issue: GitHub.