linera-io/linera-protocol · error · Error

Invalid owner address

Error message

Invalid owner address

What it means

After confirming the wallet holds a key for the owner, prepare_for_owner validates that the owner may actually propose: ownership.can_propose_in_multi_leader_round(&owner) must be true, i.e. the owner is in the chain's regular/super owners, or the chain explicitly allows any key via open_multi_leader_rounds. Error::NotAnOwner means the owner is a nobody on this chain and public rounds are closed, so proposals from them would be rejected by validators anyway.

Source

Thrown at web/@linera/client/src/signer/WebCryptoEd25519.ts:150

    const buf = new Uint8Array(value).buffer as ArrayBuffer;
    const sig = await crypto.subtle.sign("Ed25519", this.record.privateKey, buf);
    return "0x" + bytesToHex(new Uint8Array(sig));
  }

  async getPublicKey(owner: string): Promise<string> {
    this.assertOwner(owner);
    return "0x" + bytesToHex(this.record.publicKey);
  }

  async containsKey(owner: string): Promise<boolean> {
    // record.owner is canonical lowercase; only normalize the caller side.
    return owner.toLowerCase() === this.record.owner;
  }

  private assertOwner(owner: string): void {
    // record.owner is canonical lowercase; only normalize the caller side.
    if (owner.toLowerCase() !== this.record.owner) {
      throw new Error("Invalid owner address");
    }
  }
}

const DB_NAME = "linera-signer";
const STORE_NAME = "keys";
const DB_VERSION = 1;

function openDb(): Promise<IDBDatabase> {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open(DB_NAME, DB_VERSION);
    req.onupgradeneeded = () => {
      const db = req.result;
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        db.createObjectStore(STORE_NAME);
      }
    };
    // Fires when another open connection on a lower version blocks this upgrade.

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Register the owner on the chain first: call share_ownership(new_owner, weight) from an existing owner, or add it via an ownership update operation.
  2. If the chain is meant to be openly proposeable, create/configure it with open_multi_leader_rounds (faucet parameter) so can_propose_in_multi_leader_round passes for any signer.
  3. Switch the client to one of the chain's actual owners (check chain_info().manager.ownership.owners).
  4. If you were removed as owner, ask a current owner to re-add you; do not retry with the stale key.

Example fix

// before: non-owner key attempts to propose
let info = client.prepare_for_owner(not_an_owner).await?; // NotAnOwner

// after: an existing owner shares ownership first
owner_client.share_ownership(not_an_owner, 100).await?;
let info = client.prepare_for_owner(not_an_owner).await?;
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the owner may propose (owner or open multi-leader rounds) beforehand
let ownership = client.chain_info().await?.manager.ownership;
if !ownership.can_propose_in_multi_leader_round(&owner) {
    return Err(anyhow::anyhow!("owner {owner} cannot propose on {}; register it or open multi-leader rounds", client.chain_id()));
}

Type guard

pub async fn can_owner_propose(client: &ChainClient, owner: AccountOwner) -> Result<bool, Error> {
    Ok(client.chain_info().await?.manager.ownership.can_propose_in_multi_leader_round(&owner))
}

Try / catch

match client.prepare_for_owner(owner).await {
    Ok(info) => info,
    Err(Error::NotAnOwner(chain_id)) => {
        // register the owner via share_ownership from an existing owner client, then retry
        return Err(anyhow::anyhow!("owner not registered on {chain_id}; call share_ownership first"));
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling prepare_for_owner on a chain where the owner is not listed in ownership and open_multi_leader_rounds is disabled. Producers: assign_new_chain_to_key / test_open_multi_leader_rounds with a non-owner key; a client whose owner was removed from ownership (weight 0) but still tries to propose.

Common situations: Multi-leader chains created by a faucet without open rounds enabled; owner rotation where the old owner still runs a client; copy-pasting an owner address instead of registering it on the chain first.

Related errors


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