linera-io/linera-protocol · error

Failed to save claims to database: {e}

Error message

Failed to save claims to database: {e}

What it means

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.

Source

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

                return Ok(());
            }
            self.faucet_storage
                .store_chains_batch(initial_chains, block_timestamp)
                .await
        };
        let store_daily = async {
            if daily_claims.is_empty() {
                return Ok(());
            }
            self.faucet_storage
                .store_daily_claims_batch(daily_claims)
                .await
        };

        if let Err(e) = futures::try_join!(store_initial, store_daily) {
            let error_msg = format!("Failed to save claims to database: {e}");
            Self::send_err(requests, error_msg.clone());
            anyhow::bail!(error_msg);
        }

        // Respond to requests.
        #[cfg(with_metrics)]
        let chains_created = initial_desc_map.len();

        for request in requests {
            #[cfg(with_metrics)]
            {
                let wait_time = request.queued_at.elapsed().as_secs_f64() * 1000.0;
                metrics::QUEUE_WAIT_TIME
                    .with_label_values(&[])
                    .observe(wait_time);
            }

            let response = if let Some(target_chain_id) = request.target_chain_id {
                PendingResponse::Daily(Ok(ClaimOutcome {
                    chain_id: target_chain_id,

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Check the {e} inner error in faucet logs — it is the storage backend's message (table missing, auth, throttling).
  2. Restore DB connectivity/capacity (start local Dynamo, fix credentials/tables, raise write capacity).
  3. 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.
  4. Restart the faucet; queued requests re-attempt since failed responses were already sent.

Example fix

// before
if let Err(e) = futures::try_join!(store_initial, store_daily) {
    let error_msg = format!("Failed to save claims to database: {e}");
    Self::send_err(requests, error_msg.clone());
    anyhow::bail!(error_msg);
}

// after: retry transient storage failures before giving up (on-chain state already committed)
let mut attempt = 0;
loop {
    let r = futures::try_join!(store_initial(), store_daily());
    match r {
        Ok(()) => break,
        Err(e) if attempt < 3 => { attempt += 1; tokio::time::sleep(Duration::from_secs(2_u64.pow(attempt))).await; }
        Err(e) => { let m = format!("Failed to save claims to database: {e}"); Self::send_err(requests, m.clone()); anyhow::bail!(m); }
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight storage health before accepting claims:
self.faucet_storage.store_daily_claims_batch(vec![]).await?; // or an explicit ping/health call

Try / catch

// On 'Failed to save claims to database': retry with backoff (transient DB outage);
// after max attempts, reconcile manually — on-chain chains exist but the DB lacks records,
// so re-processing the same requests must be idempotent (key by owner/period).

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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