{"record":{"id":"56a2a2348a099db9","repo":"linera-io/linera-protocol","slug":"failed-to-save-claims-to-database-e","errorCode":null,"errorMessage":"Failed to save claims to database: {e}","messagePattern":"Failed to save claims to database: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-faucet/server/src/lib.rs","lineNumber":1083,"sourceCode":"                return Ok(());\n            }\n            self.faucet_storage\n                .store_chains_batch(initial_chains, block_timestamp)\n                .await\n        };\n        let store_daily = async {\n            if daily_claims.is_empty() {\n                return Ok(());\n            }\n            self.faucet_storage\n                .store_daily_claims_batch(daily_claims)\n                .await\n        };\n\n        if let Err(e) = futures::try_join!(store_initial, store_daily) {\n            let error_msg = format!(\"Failed to save claims to database: {e}\");\n            Self::send_err(requests, error_msg.clone());\n            anyhow::bail!(error_msg);\n        }\n\n        // Respond to requests.\n        #[cfg(with_metrics)]\n        let chains_created = initial_desc_map.len();\n\n        for request in requests {\n            #[cfg(with_metrics)]\n            {\n                let wait_time = request.queued_at.elapsed().as_secs_f64() * 1000.0;\n                metrics::QUEUE_WAIT_TIME\n                    .with_label_values(&[])\n                    .observe(wait_time);\n            }\n\n            let response = if let Some(target_chain_id) = request.target_chain_id {\n                PendingResponse::Daily(Ok(ClaimOutcome {\n                    chain_id: target_chain_id,","sourceCodeStart":1065,"sourceCodeEnd":1101,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-faucet/server/src/lib.rs#L1065-L1101","documentation":"The faucet successfully committed the block that created chains / granted tokens on-chain, but then failed to persist the records to its own database: futures::try_join!(store_chains_batch, store_daily_claims_batch) errored. On-chain state and the faucet's DB are now out of sync — chains exist but are not recorded, which breaks the faucet's rate limiting and idempotency bookkeeping. The error message is sent to all waiting requesters and the batch processing aborts.","triggerScenarios":"process_batch commits the certificate, then store_chains_batch or store_daily_claims_batch hits a storage failure: DynamoDB/table not created, connection dropped, permission denied, write-capacity exceeded, or schema mismatch after a faucet version change.","commonSituations":"AWS DynamoDB (or configured store) credentials/tables missing or misconfigured; DB briefly unavailable during a batch; faucet upgraded without running the storage migration; local Dynamo not started in dev.","solutions":["Check the {e} inner error in faucet logs — it is the storage backend's message (table missing, auth, throttling).","Restore DB connectivity/capacity (start local Dynamo, fix credentials/tables, raise write capacity).","Reconcile: users whose chains were created but not recorded — they will be told the claim failed, and may need the record inserted manually or the daily-limit reset, otherwise re-claiming an existing chain can misbehave.","Restart the faucet; queued requests re-attempt since failed responses were already sent."],"exampleFix":"// before\nif let Err(e) = futures::try_join!(store_initial, store_daily) {\n    let error_msg = format!(\"Failed to save claims to database: {e}\");\n    Self::send_err(requests, error_msg.clone());\n    anyhow::bail!(error_msg);\n}\n\n// after: retry transient storage failures before giving up (on-chain state already committed)\nlet mut attempt = 0;\nloop {\n    let r = futures::try_join!(store_initial(), store_daily());\n    match r {\n        Ok(()) => break,\n        Err(e) if attempt < 3 => { attempt += 1; tokio::time::sleep(Duration::from_secs(2_u64.pow(attempt))).await; }\n        Err(e) => { let m = format!(\"Failed to save claims to database: {e}\"); Self::send_err(requests, m.clone()); anyhow::bail!(m); }\n    }\n}","handlingStrategy":"retry","validationCode":"// Pre-flight storage health before accepting claims:\nself.faucet_storage.store_daily_claims_batch(vec![]).await?; // or an explicit ping/health call","typeGuard":null,"tryCatchPattern":"// On 'Failed to save claims to database': retry with backoff (transient DB outage);\n// after max attempts, reconcile manually — on-chain chains exist but the DB lacks records,\n// so re-processing the same requests must be idempotent (key by owner/period).","preventionTips":["Provision/verify DB tables and capacity before opening the faucet (migration step in deployment).","Alert on storage errors so drift is caught before users re-claim created chains.","Make claim storage idempotent (upsert by owner + daily period) so retries after partial failure are safe."],"tags":["linera-faucet","database","persistence","consistency","rust"],"backgroundTag":"database-write-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}