{"record":{"id":"fdc2f8a04f0bbf0c","repo":"linera-io/linera-protocol","slug":"metamask-is-not-connected-with-the-requested-owner","errorCode":null,"errorMessage":"MetaMask is not connected with the requested owner: ${owner}","messagePattern":"MetaMask is not connected with the requested owner: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/@linera/metamask/src/signer.ts","lineNumber":57,"sourceCode":"  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\n    const connected = accounts.find(\n      (acc) => acc.toLowerCase() === owner.toLowerCase(),\n    );\n    if (!connected) {\n      throw new Error(\n        `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) {","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/web/@linera/metamask/src/signer.ts#L39-L75","documentation":"revoke_epochs builds an Admin operation that revokes all epochs up to and including revoked_epoch. The guard ensure!(revoked_epoch < current_epoch) rejects revoking the epoch the chain is currently in (or any future epoch): the current epoch's committee is still signing blocks, so revoking it would break consensus. Only strictly older epochs may be revoked.","triggerScenarios":"Calling revoke_epochs(epoch) where epoch >= chain_info().epoch — typically passing the current epoch (e.g. Epoch(0) on a fresh network where current is also 0). Producers: governance scripts that compute 'revoke up to now' instead of 'up to previous'; off-by-one loops over 0..=current_epoch.","commonSituations":"Test suites revoking epochs after creating a committee without advancing to a new epoch; operators cleaning old committee data on a young network; version changes in the admin API where the epoch argument semantics shifted.","solutions":["Pass an epoch strictly lower than the current one: use revoke_epochs(Epoch(current.0.saturating_sub(1))) at most, and only if current > 0.","If you intend to retire the current committee, first create and migrate to a new epoch, then revoke the old one.","Read the chain's current epoch via chain_info().epoch before constructing the call and validate the bound in your script.","Check for off-by-one in loops: iterate 0..revoked_epoch.0 after validating revoked_epoch < current_epoch."],"exampleFix":"// before: revoking the epoch the chain is still using\nclient.revoke_epochs(current_epoch).await?; // CannotRevokeCurrentEpoch\n\n// after: revoke only strictly older epochs\nlet current = client.chain_info().await?.epoch;\nif revoked_epoch < current {\n    client.revoke_epochs(revoked_epoch).await?;\n}","handlingStrategy":"validation","validationCode":"// Validate the epoch bound before revoking\nlet current = client.chain_info().await?.epoch;\nif revoked_epoch >= current {\n    return Err(anyhow::anyhow!(\"can only revoke epochs < current ({current}); got {revoked_epoch}\"));\n}","typeGuard":"pub async fn can_revoke_epoch(client: &ChainClient, epoch: Epoch) -> Result<bool, Error> {\n    Ok(epoch < client.chain_info().await?.epoch)\n}","tryCatchPattern":"match client.revoke_epochs(revoked_epoch).await {\n    Ok(outcome) => outcome,\n    Err(Error::CannotRevokeCurrentEpoch(current)) => {\n        // wait for a new epoch, then revoke the old one\n        return Err(anyhow::anyhow!(\"epoch {current} is current; create the next epoch before revoking\"));\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always fetch chain_info().epoch and assert revoked_epoch < current before calling revoke_epochs.","Use half-open ranges (0..current) in governance scripts to avoid off-by-one.","Migrate to a new epoch first when retiring the current committee.","Unit-test epoch arithmetic around reconfiguration in governance tooling."],"tags":["linera","epoch","governance","reconfiguration","validation"],"backgroundTag":"epoch-revocation","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}