{"record":{"id":"0b785a67fea88952","repo":"linera-io/linera-protocol","slug":"no-signer-found-for-owner-owner","errorCode":null,"errorMessage":"no signer found for owner ${owner}","messagePattern":"no signer found for owner (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/@linera/client/src/signer/Composite.ts","lineNumber":18,"sourceCode":"import type { Signer } from \"./Signer.d.ts\";\n\n/**\n * A signer implementation that tries multiple signers in series.\n */\nexport default class Composite implements Signer {\n  private signers: Signer[];\n\n  constructor(...signers: Signer[]) {\n    this.signers = signers;\n  }\n\n  async sign(owner: string, value: Uint8Array): Promise<string> {\n    for (const signer of this.signers)\n      if (await signer.containsKey(owner))\n        return await signer.sign(owner, value);\n\n    throw new Error(`no signer found for owner ${owner}`);\n  }\n\n  async getPublicKey(owner: string): Promise<string> {\n    for (const signer of this.signers)\n      if (await signer.containsKey(owner))\n        return await signer.getPublicKey(owner);\n\n    throw new Error(`no signer found for owner ${owner}`);\n  }\n\n  async containsKey(owner: string): Promise<boolean> {\n    for (const signer of this.signers)\n      if (await signer.containsKey(owner))\n        return true;\n\n    return false;\n  }\n}","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/web/@linera/client/src/signer/Composite.ts#L1-L36","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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).","Run `linera retry-pending-block` if the divergence comes from one of your own unconfirmed proposals still pending on the chain.","Verify you are pointed at a fully caught-up validator/shard (proxy/grpc endpoint) and not a stale replica."],"exampleFix":"// before: building a proposal from a stale ChainInfo\nlet info = client.chain_info().await?;\nlet block = client.prepare_block(&operations).await?; // may hit UnexpectedBlockHeight\n\n// after: sync the tip with validators first, then propose\nclient.synchronize_from_validators().await?;\nlet block = client.prepare_block(&operations).await?;","handlingStrategy":"retry","validationCode":"// Pre-check the tip matches your expectation before proposing/querying with test_next_block_height\nlet info = client.chain_info().await?;\nif info.next_block_height != expected_next_block_height {\n    client.synchronize_from_validators().await?;\n}","typeGuard":"pub async fn is_tip_at_height(client: &ChainClient, height: BlockHeight) -> Result<bool, Error> {\n    Ok(client.chain_info().await?.next_block_height == height)\n}","tryCatchPattern":"match result {\n    Ok(v) => v,\n    Err(Error::WorkerError(WorkerError::UnexpectedBlockHeight { expected_block_height, found_block_height })) => {\n        // resync and rebuild the proposal on the new tip\n        client.synchronize_from_validators().await?;\n        /* retry once with fresh chain info */\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always call synchronize_from_validators() (or synchronize_up_to) right before building a block proposal.","Never reuse a ChainInfo snapshot across awaits where other writers may advance the chain.","Keep one proposer per chain in your application to avoid racing proposals.","Monitor next_block_height changes on long-running clients and refresh on any mismatch."],"tags":["linera","block-height","consensus","state-divergence","stale-state"],"backgroundTag":"block-height-mismatch","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}