{"record":{"id":"dc301d0c189ae243","repo":"linera-io/linera-protocol","slug":"metamask-is-not-available","errorCode":null,"errorMessage":"MetaMask is not available","messagePattern":"MetaMask is not available","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/@linera/metamask/src/signer.ts","lineNumber":34,"sourceCode":" * and interacts with it using EIP-191-compliant requests. It provides a secure\n * mechanism for message signing through the user's MetaMask wallet.\n * \n * ⚠️ WARNING: This signer requires MetaMask to be installed and unlocked in the browser.\n * It will throw errors if MetaMask is unavailable, the user rejects a request, or\n * if the requested signer is not among the connected accounts.\n * \n * The `MetaMask` signer verifies that the connected account matches the specified\n * owner address before signing a message. All messages are encoded as hexadecimal\n * strings and signed using the `personal_sign` method.\n * \n * Suitable for production use where MetaMask is the expected signer interface.\n */\nexport default class Signer implements SignerInterface {\n  private provider: ethers.BrowserProvider;\n\n  constructor() {\n    if (typeof window === \"undefined\" || !window.ethereum) {\n      throw new Error(\"MetaMask is not available\");\n    }\n    this.provider = new ethers.BrowserProvider(window.ethereum!);\n  }\n\n  async sign(owner: string, value: Uint8Array): Promise<string> {\n    if (!window.ethereum) {\n      throw new Error(\"MetaMask is not available\");\n    }\n\n    // Explicitly type the result and check for undefined\n    const accounts = (await window.ethereum.request({\n      method: \"eth_requestAccounts\",\n    })) as string[] | undefined;\n\n    if (!accounts || accounts.length === 0) {\n      throw new Error(\"No MetaMask accounts connected\");\n    }\n","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/web/@linera/metamask/src/signer.ts#L16-L52","documentation":"The ChainClient guards concurrent proposals with a PendingProposal. If you try to build/propose a new block while a previous proposal is still pending (sent but not yet confirmed by a quorum), prepare... returns Error::BlockProposalError with this message. The pending block must be resolved first — either confirmed via certificates from validators, or explicitly retried — because two in-flight proposals for the same height cannot both succeed.","triggerScenarios":"Calling the block-preparation entry point (which takes proposal_guard: &mut Option<PendingProposal>) twice without the first proposal being finalized: e.g. propose a block, the client crashes or the network stalls before confirmation, then call the same flow again. The guard ensure!(proposal_guard.is_none()) fires.","commonSituations":"Process restart after a failed submit; validator timeouts leaving the block unconfirmed; automation loops that re-issue 'transfer' commands while a previous one is in flight; the JSON-RPC service receiving two concurrent requests for the same chain.","solutions":["Run `linera retry-pending-block [CHAIN_ID]` (or client retry_pending_block / the node-service retry_pending_block endpoint) to commit or discard the outstanding proposal first.","If the pending block is obsolete, explicitly drop it and then propose the new block in a fresh call.","Serialize proposals per chain in your application (one in-flight proposal at a time) so the guard never trips.","After retrying, verify the block got confirmed by checking next_block_height advanced before issuing new operations."],"exampleFix":"# before: issuing a new operation while a proposal is pending\nlinera transfer --amount 5 ... # BlockProposalError: already has a pending block\n\n# after: settle the pending block first\nlinera retry-pending-block <CHAIN_ID>\nlinera transfer --amount 5 ...","handlingStrategy":"retry","validationCode":"// Application-level guard: serialize proposals per chain\nif proposal_in_flight.contains(&chain_id) {\n    return Err(anyhow::anyhow!(\"a proposal is already pending for {chain_id}; finish it first\"));\n}","typeGuard":null,"tryCatchPattern":"match client.prepare_block(operations).await {\n    Ok(block) => block,\n    Err(Error::BlockProposalError(msg)) if msg.contains(\"pending block\") => {\n        client.retry_pending_block().await?; // or node-service retry_pending_block\n        client.prepare_block(operations).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep at most one in-flight block proposal per chain (per-process mutex or cross-process lock).","Run `linera retry-pending-block` after any failed or interrupted submit before issuing new operations.","On restart, check for pending proposals as part of client initialization.","Treat BlockProposalError with this message as recoverable, not fatal: finish the old block, then continue."],"tags":["linera","pending-block","block-proposal","concurrency","client-state"],"backgroundTag":"pending-block-conflict","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}