linera-io/linera-protocol · error
default chain requested but none set
Error message
default chain requested but none set
What it means
linera-client's ClientContext stores the default chain as an Option<ChainId> and default_chain() (linera-client/src/client_context.rs:395) unwraps it unconditionally. The panic fires when the context was built without a default chain - typically a wallet that has no user chain selected, such as a fresh wallet or one holding only the admin chain. Every API that needs an implicit chain (default_account, ownership, change_ownership, set_preferred_owner, the benchmark helpers, supply_fungible_tokens) routes through this accessor, so any of them can trigger it.
Source
Thrown at linera-client/src/client_context.rs:397
pub fn wallet(&self) -> &Env::Wallet {
self.client.wallet()
}
/// Returns the ID of the admin chain.
pub fn admin_chain_id(&self) -> ChainId {
self.client.admin_chain_id()
}
/// Retrieve the default account. Current this is the common account of the default
/// chain.
pub fn default_account(&self) -> Account {
Account::chain(self.default_chain())
}
/// Retrieve the default chain.
pub fn default_chain(&self) -> ChainId {
self.default_chain
.expect("default chain requested but none set")
}
/// Returns the lowest non-admin chain ID in the wallet.
pub async fn first_non_admin_chain(&self) -> Result<ChainId, Error> {
let admin_chain_id = self.admin_chain_id();
let chain_ids = self
.wallet()
.chain_ids()
.try_filter(|chain_id| futures::future::ready(*chain_id != admin_chain_id))
.try_collect::<Vec<ChainId>>()
.await
.map_err(Error::wallet)?;
Ok(chain_ids
.into_iter()
.min()
.expect("No non-admin chain specified in wallet with no non-admin chain"))
}
View on GitHub (pinned to 6c226ddcb3)
Solutions
- Claim or create a user chain (e.g. via the faucet) so the wallet holds a non-admin chain, then set it as the default chain in the wallet.
- Re-run the command with an explicit --chain-id so default_chain() is never consulted.
- Inspect the wallet (linera wallet show) and assign a default chain pointing at an existing chain entry.
- If embedding linera-client, populate the default chain from wallet.chain_ids() before calling any default-chain-dependent API.
Example fix
// before
let account = context.default_account(); // panics: "default chain requested but none set"
// after
// Option<ChainId> field on the context options; check it first
match context_options.default_chain {
Some(chain_id) => Account::chain(chain_id),
None => return Err(anyhow::anyhow!("no default chain in wallet; pass --chain-id or claim a chain first")),
} Defensive patterns
Strategy: validation
Validate before calling
// Before any default-chain-dependent call, verify the wallet has a usable chain:
let chain_ids: Vec<ChainId> = wallet.chain_ids().try_collect().await?;
let has_user_chain = chain_ids.iter().any(|id| *id != client_context.admin_chain_id());
if !has_user_chain {
anyhow::bail!("wallet holds no non-admin chain; claim one from the faucet or pass --chain-id");
} Type guard
fn has_default_chain(ctx: &ClientContext) -> bool {
// default_chain is Option<ChainId> on the context/options; treat None as unsafe to call
ctx_default_chain_option(ctx).is_some()
}
fn ctx_default_chain_option(ctx: &ClientContext) -> Option<ChainId> {
// if the field is not directly accessible, mirror it via wallet state:
// Some(chain) only when the wallet marks a chain as default/preferred.
ctx.default_chain
} Try / catch
// Panics are not Result-based; when embedding, isolate with catch_unwind at the boundary:
let result = std::panic::catch_unwind(AssertUnwindSafe(|| context.default_account()));
match result {
Ok(account) => { /* proceed */ }
Err(_) => return Err(anyhow::anyhow!("no default chain configured in wallet")),
} Prevention
- Always pass an explicit --chain-id in scripts instead of relying on the implicit default chain.
- After wallet creation, verify with `linera wallet show` that a default chain is set before running commands.
- When embedding linera-client, populate the default chain from wallet.chain_ids() at context construction and fail fast if empty.
When it happens
Trigger: Calling default_account(), ownership(), change_ownership(), set_preferred_owner(), or the benchmark preparation flow while wallet/config left default_chain as None. Concretely: running a wallet-dependent CLI command against a wallet whose only entry is the admin chain, or constructing ClientContext programmatically with default_chain: None (as the unit-test stub in linera-client/src/unit_tests/client_context.rs does).
Common situations: Fresh wallet created but no user chain claimed from the faucet yet; a wallet whose default chain entry was removed or lost after migration; CLI wallet generation that omitted a default chain assignment; embedding linera-client and forgetting to derive the default chain from wallet.chain_ids() before use.
Related errors
- No non-admin chain specified in wallet with no non-admin cha
- owner should be different from spender
- invalid block export configuration: {message}
- Returned AccountInfo should have code: Some(...) and so code
- Returned AccountInfo should have code: Some(...) and so code
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/f5b327a2fa0e7019.
Report an issue: GitHub.