{"record":{"id":"37a354d251f3b3db","repo":"linera-io/linera-protocol","slug":"you-must-claim-a-chain-before-making-daily-claims","errorCode":null,"errorMessage":"You must claim a chain before making daily claims","messagePattern":"You must claim a chain before making daily claims","errorType":"validation","errorClass":"async_graphql::Error","httpStatus":null,"severity":"warning","filePath":"linera-faucet/server/src/lib.rs","lineNumber":572,"sourceCode":"        // under its own `result` label: these paths return before the queue round-trip\n        // that increments `CLAIM_REQUESTS_TOTAL`, so without the explicit counters\n        // refusals are invisible in metrics (they only appear as unlabelled\n        // `claim_latency_ms{result=\"error\"}` observations).\n        if self.daily_claim_amount == Amount::ZERO {\n            #[cfg(with_metrics)]\n            metrics::CLAIM_REQUESTS_TOTAL\n                .with_label_values(&[\"daily_disabled\"])\n                .inc();\n            return Err(Error::new(DAILY_DISABLED_MSG));\n        }\n\n        // The user must have done the initial claim first.\n        let Some(initial_claim) = self.faucet_storage.initial_claim(&owner).await? else {\n            #[cfg(with_metrics)]\n            metrics::CLAIM_REQUESTS_TOTAL\n                .with_label_values(&[\"daily_no_chain\"])\n                .inc();\n            return Err(Error::new(DAILY_NO_CHAIN_MSG));\n        };\n\n        let now = self.storage.clock().current_time();\n        let period = current_daily_period(initial_claim.timestamp.micros(), now.micros());\n        let last_period = self\n            .faucet_storage\n            .last_daily_claim_period(&owner)\n            .await?\n            .unwrap_or(0);\n\n        if period <= last_period {\n            #[cfg(with_metrics)]\n            metrics::CLAIM_REQUESTS_TOTAL\n                .with_label_values(&[\"daily_limit\"])\n                .inc();\n            return Err(Error::new(DAILY_LIMIT_MSG));\n        }\n","sourceCodeStart":554,"sourceCodeEnd":590,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-faucet/server/src/lib.rs#L554-L590","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call `claim(owner)` first and wait for its ChainDescription, then use the same owner for `dailyClaim`","Persist the owner key (wallet file) so the initial claim is reused instead of creating a new identity per run","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","Before retrying, verify the record exists with the `initialClaim(owner)` or `chainId(owner)` GraphQL queries"],"exampleFix":"// before - daily claim with an owner that never claimed\nlet outcome = client.daily_claim(new_owner, None).await?; // Err: You must claim a chain before making daily claims\n\n// after - complete the initial claim first, then reuse the same owner\nlet description = client.claim(owner, None).await?;\nlet outcome = client.daily_claim(owner, None).await?;","handlingStrategy":"validation","validationCode":"// Run before dailyClaim - the faucet exposes these queries:\n//   query { initialClaim(owner: \"<AccountOwner>\") { chainId timestamp } }\n//     -> null means no initial claim: call `claim` first.\n//   query { chainId(owner: \"<AccountOwner>\") }\n//     -> errors with `This user has no chain yet` when nothing is registered.","typeGuard":"fn needs_initial_claim(msg: &str) -> bool { msg == \"You must claim a chain before making daily claims\" }","tryCatchPattern":"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.","preventionTips":["Always run `claim(owner)` to completion before the first `dailyClaim` for that owner","Persist the owner key/wallet so the initial claim is reused across sessions instead of generating a new identity","Use one owner identity per user; do not mint fresh AccountOwners for daily claims"],"tags":["faucet","daily-claim","precondition","ordering","account-owner","linera"],"backgroundTag":"missing-prerequisite","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}