linera-io/linera-protocol · error

No non-admin chain specified in wallet with no non-admin cha

Error message

No non-admin chain specified in wallet with no non-admin chain

What it means

first_non_admin_chain (linera-client/src/client_context.rs:401) collects every wallet chain ID except the admin chain and takes .min() over the iterator. If the wallet contains only the admin chain (or nothing besides it), the iterator is empty, min() returns None, and expect panics. It is used by run to pick a chain to operate on, so startup of such commands aborts.

Source

Thrown at linera-client/src/client_context.rs:413

    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"))
    }

    /// Creates a node provider configured with this context's network options.
    // TODO(#5084) this should match the `NodeProvider` from the `Environment`
    pub fn make_node_provider(&self) -> NodeProvider {
        NodeProvider::new(self.make_node_options())
    }

    fn make_node_options(&self) -> NodeOptions {
        NodeOptions {
            send_timeout: self.send_timeout,
            recv_timeout: self.recv_timeout,
            retry_delay: self.retry_delay,
            max_retries: self.max_retries,
            max_backoff: self.max_backoff,
        }
    }

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Create or claim a non-admin chain into the wallet (faucet claim or wallet create with a new chain) and retry.
  2. Verify wallet contents with linera wallet show to confirm at least one non-admin chain exists.
  3. Use a wallet file that already includes user chains (point the command at the correct --wallet path).
Defensive patterns

Strategy: validation

Validate before calling

let chain_ids: Vec<ChainId> = wallet.chain_ids().try_collect().await?;
let non_admin: Vec<_> = chain_ids.into_iter().filter(|id| *id != admin_chain_id).collect();
if non_admin.is_empty() {
    anyhow::bail!("wallet has only the admin chain; create or claim a user chain first");
}
let chain = non_admin.into_iter().min().unwrap();

Type guard

fn has_non_admin_chain(chain_ids: &[ChainId], admin: ChainId) -> bool {
    chain_ids.iter().any(|id| *id != admin)
}

Try / catch

let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
    block_on(client_context.first_non_admin_chain())
}));
if let Err(_) = result {
    // wallet unusable: guide user to claim a chain instead of retrying
}

Prevention

When it happens

Trigger: Invoking a command whose run path calls first_non_admin_chain() while the wallet's chain_ids() yield only the admin chain. Typical with a newly generated devnet/test wallet before any user chain is created or claimed.

Common situations: Wallet generated with only the admin (root) chain; user chains were pruned from the wallet file; running faucet-less local setups where chains were expected to be auto-created but were not.

Related errors


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