{"record":{"id":"b2574216eca751ae","repo":"linera-io/linera-protocol","slug":"metamask-signature-request-failed-err-message","errorCode":null,"errorMessage":"MetaMask signature request failed: ${err?.message || err}","messagePattern":"MetaMask signature request failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/@linera/metamask/src/signer.ts","lineNumber":76,"sourceCode":"        `MetaMask is not connected with the requested owner: ${owner}`,\n      );\n    }\n\n    // Encode message as hex string\n    const msgHex = `0x${uint8ArrayToHex(value)}`;\n    try {\n      const signature = (await window.ethereum.request({\n        method: \"personal_sign\",\n        params: [msgHex, owner],\n      })) as string;\n\n      if (!signature) {\n        throw new Error(\"No signature returned\");\n      }\n\n      return signature;\n    } catch (err: any) {\n      throw new Error(\n        `MetaMask signature request failed: ${err?.message || err}`,\n      );\n    }\n  }\n\n  async getPublicKey(_owner: string): Promise<string> {\n    // MetaMask signs only for `Address20` (EVM secp256k1) owners. The wasm bridge\n    // never calls `getPublicKey` on the Address20 path — EVM signatures carry the\n    // signer's address inline in `AccountSignature::EvmSecp256k1`. If we get here,\n    // a caller is reaching past the bridge contract.\n    throw new Error(\n      \"MetaMask signer does not expose a public key; EVM signatures carry the address\",\n    );\n  }\n\n  async containsKey(owner: string): Promise<boolean> {\n    const accounts = await this.provider.send(\"eth_requestAccounts\", []);\n    return accounts.some(","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/web/@linera/metamask/src/signer.ts#L58-L94","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nthrow new Error(`MetaMask signature request failed: ${err?.message || err}`); // code lost\n\n// after\nif (err?.code === 4001) throw new Error('User rejected the signature request');\nthrow new Error(`MetaMask signature request failed: ${err?.message || err}`);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { const sig = await signer.sign(owner, bytes); }\ncatch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  if (msg.includes('user rejected') || msg.includes('4001')) { /* benign cancel: re-prompt with context */ }\n  else { /* surface wallet/network failure */ }\n}","preventionTips":["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."],"tags":["metamask","signature","user-rejection","error-wrapping"],"backgroundTag":"wallet-signing-rejected","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}