linera-io/linera-protocol · warning · async_graphql::Error

You must claim a chain before making daily claims

Error message

You must claim a chain before making daily claims

What it means

`dailyClaim` requires a completed initial claim: the faucet looks up `initial_claim(&owner)` in its database and refuses with DAILY_NO_CHAIN_MSG when no record exists. The owner is the AccountOwner key used in the original `claim` — the daily transfer goes to the chain created by that first claim. Calling `dailyClaim` with a fresh owner, or before `claim` has returned, produces this error.

Source

Thrown at linera-faucet/server/src/lib.rs:572

        // under its own `result` label: these paths return before the queue round-trip
        // that increments `CLAIM_REQUESTS_TOTAL`, so without the explicit counters
        // refusals are invisible in metrics (they only appear as unlabelled
        // `claim_latency_ms{result="error"}` observations).
        if self.daily_claim_amount == Amount::ZERO {
            #[cfg(with_metrics)]
            metrics::CLAIM_REQUESTS_TOTAL
                .with_label_values(&["daily_disabled"])
                .inc();
            return Err(Error::new(DAILY_DISABLED_MSG));
        }

        // The user must have done the initial claim first.
        let Some(initial_claim) = self.faucet_storage.initial_claim(&owner).await? else {
            #[cfg(with_metrics)]
            metrics::CLAIM_REQUESTS_TOTAL
                .with_label_values(&["daily_no_chain"])
                .inc();
            return Err(Error::new(DAILY_NO_CHAIN_MSG));
        };

        let now = self.storage.clock().current_time();
        let period = current_daily_period(initial_claim.timestamp.micros(), now.micros());
        let last_period = self
            .faucet_storage
            .last_daily_claim_period(&owner)
            .await?
            .unwrap_or(0);

        if period <= last_period {
            #[cfg(with_metrics)]
            metrics::CLAIM_REQUESTS_TOTAL
                .with_label_values(&["daily_limit"])
                .inc();
            return Err(Error::new(DAILY_LIMIT_MSG));
        }

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Call `claim(owner)` first and wait for its ChainDescription, then use the same owner for `dailyClaim`
  2. Persist the owner key (wallet file) so the initial claim is reused instead of creating a new identity per run
  3. If the faucet DB was reset but you still hold the chain, re-run `claim` — the duplicate path returns the existing chain description from storage
  4. Before retrying, verify the record exists with the `initialClaim(owner)` or `chainId(owner)` GraphQL queries

Example fix

// before - daily claim with an owner that never claimed
let outcome = client.daily_claim(new_owner, None).await?; // Err: You must claim a chain before making daily claims

// after - complete the initial claim first, then reuse the same owner
let description = client.claim(owner, None).await?;
let outcome = client.daily_claim(owner, None).await?;
Defensive patterns

Strategy: validation

Validate before calling

// Run before dailyClaim - the faucet exposes these queries:
//   query { initialClaim(owner: "<AccountOwner>") { chainId timestamp } }
//     -> null means no initial claim: call `claim` first.
//   query { chainId(owner: "<AccountOwner>") }
//     -> errors with `This user has no chain yet` when nothing is registered.

Type guard

fn needs_initial_claim(msg: &str) -> bool { msg == "You must claim a chain before making daily claims" }

Try / catch

Catch the `dailyClaim` error; on `You must claim a chain before making daily claims`, run `claim(owner)` with the SAME owner, wait for the ChainDescription, then retry `dailyClaim` once.

Prevention

When it happens

Trigger: Calling `dailyClaim(owner)` where that exact AccountOwner never completed a `claim`; generating a new key/wallet per session and using it for daily claims; the faucet's claim database having been reset so the initial-claim record is gone even though the chain still exists on-chain.

Common situations: Example apps and bots that regenerate a keypair on every run; a client firing `dailyClaim` before the initial claim's ChainDescription came back; faucet database wiped/recreated during maintenance so owner→chain mappings were lost.

Related errors


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