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
- 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.
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
- 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.
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
- Chain is expecting a next block at height {expected_block_he
- BlockHeightOverflow
- UnexpectedBlockHeight
- Invalid owner address
- CannotRejectMessage
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/0b785a67fea88952.
Report an issue: GitHub.