linera-io/linera-protocol · error · Error

no signer found for owner ${owner}

Error message

no signer found for owner ${owner}

What it means

The chain worker expected the chain's next block at a given height, but the queried height does not match. It is raised while answering a ChainInfoQuery that carries test_next_block_height: the client (usually during block proposal) asks the worker to confirm that the chain's tip is at next_block_height, and the worker's local state disagrees. In practice it means the client's view of the chain tip is stale or ahead of the node/worker it is talking to (e.g. another proposal was already confirmed). It is a consensus-state divergence check, not a network failure.

Source

Thrown at web/@linera/client/src/signer/Composite.ts:18

import type { Signer } from "./Signer.d.ts";

/**
 * A signer implementation that tries multiple signers in series.
 */
export default class Composite implements Signer {
  private signers: Signer[];

  constructor(...signers: Signer[]) {
    this.signers = signers;
  }

  async sign(owner: string, value: Uint8Array): Promise<string> {
    for (const signer of this.signers)
      if (await signer.containsKey(owner))
        return await signer.sign(owner, value);

    throw new Error(`no signer found for owner ${owner}`);
  }

  async getPublicKey(owner: string): Promise<string> {
    for (const signer of this.signers)
      if (await signer.containsKey(owner))
        return await signer.getPublicKey(owner);

    throw new Error(`no signer found for owner ${owner}`);
  }

  async containsKey(owner: string): Promise<boolean> {
    for (const signer of this.signers)
      if (await signer.containsKey(owner))
        return true;

    return false;
  }
}

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Refresh the client's view of the chain before proposing: call client.synchronize_from_validators() (or synchronize_up_to(target_height)) so the local chain tip matches the validators.
  2. If another proposal won the race, drop your stale proposal and re-build it on top of the newly confirmed block (re-read chain_info and re-execute the operations).
  3. Run `linera retry-pending-block` if the divergence comes from one of your own unconfirmed proposals still pending on the chain.
  4. Verify you are pointed at a fully caught-up validator/shard (proxy/grpc endpoint) and not a stale replica.

Example fix

// before: building a proposal from a stale ChainInfo
let info = client.chain_info().await?;
let block = client.prepare_block(&operations).await?; // may hit UnexpectedBlockHeight

// after: sync the tip with validators first, then propose
client.synchronize_from_validators().await?;
let block = client.prepare_block(&operations).await?;
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check the tip matches your expectation before proposing/querying with test_next_block_height
let info = client.chain_info().await?;
if info.next_block_height != expected_next_block_height {
    client.synchronize_from_validators().await?;
}

Type guard

pub async fn is_tip_at_height(client: &ChainClient, height: BlockHeight) -> Result<bool, Error> {
    Ok(client.chain_info().await?.next_block_height == height)
}

Try / catch

match result {
    Ok(v) => v,
    Err(Error::WorkerError(WorkerError::UnexpectedBlockHeight { expected_block_height, found_block_height })) => {
        // resync and rebuild the proposal on the new tip
        client.synchronize_from_validators().await?;
        /* retry once with fresh chain info */
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling a block-proposal path (or ChainInfoQuery with test_next_block_height set) when chain.tip_state.get().next_block_height on the worker differs from the next_block_height value the client supplied. Typical producers: proposing a block after another owner/instance already advanced the chain; proposing into a local node that is behind the validators; a client re-using an old ChainInfo snapshot to build a new proposal.

Common situations: Running several clients (or wallet + service) against the same chain where one advanced it; a local node lagging behind validators after downtime; re-loading a wallet whose trusted state is newer than the local node's indexes; concurrency where two proposals race.

Related errors


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