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

  1. 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.
  2. Fire the sign request from a user gesture with context so users accept it.
  3. 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

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


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/b2574216eca751ae. Report an issue: GitHub.