linera-io/linera-protocol · warning · async_graphql::Error
This user already has a chain
Error message
This user already has a chain
What it means
Returned by the faucet's batch validation when an initial `claim` request reaches execution although the owner already has a chain in faucet storage. The front-door check in `do_claim` normally makes `claim` idempotent — it returns the existing ChainDescription — so this message (`DUPLICATE_CHAIN_MSG`) only appears when a duplicate races past the front door (two concurrent claims for the same owner) and is refused during `validate_request`. The requester side classifies this message as 'duplicate' in metrics.
Source
Thrown at linera-faucet/server/src/lib.rs:866
let now = self.client.storage_client().clock().current_time();
let period = current_daily_period(initial_claim.timestamp.micros(), now.micros());
let last_period = self
.faucet_storage
.last_daily_claim_period(&request.owner)
.await?
.unwrap_or(0);
if period <= last_period {
return Err(Error::new(DAILY_LIMIT_MSG));
}
} else {
match self.faucet_storage.get_chain_id(&request.owner).await {
Ok(None) => {}
Ok(Some(_)) => {
// Not counted here: the requester side classifies this
// refusal by message and counts it once as "duplicate"
// (counting at both sites double-counted the request).
return Err(Error::new(DUPLICATE_CHAIN_MSG));
}
Err(err) => {
tracing::error!("Database error: {err}");
return Err(Error::new(err.to_string()));
}
}
}
Ok(())
}
/// Checks if the given requests can currently be fulfilled, based on the balance
/// and rate limiting settings. Returns an error if not.
async fn check_rate_limiting(&self, requests: &[PendingRequest]) -> async_graphql::Result<()> {
let end_timestamp = self.config.end_timestamp;
let start_timestamp = self.config.start_timestamp;
let local_time = self.client.storage_client().clock().current_time();
let full_duration = end_timestamp.delta_since(start_timestamp).as_micros();
let remaining_duration = end_timestamp.delta_since(local_time).as_micros();View on GitHub (pinned to 6c226ddcb3)
Solutions
- Retry the `claim` mutation: with the chain now recorded, the front door returns the existing ChainDescription instead of erroring.
- De-duplicate claim requests client-side — keep at most one in-flight claim per owner.
- Treat this message as 'already claimed' and re-fetch the existing chain, not as a hard failure.
Example fix
// before
const d1 = claim(owner); const d2 = claim(owner); // second may hit 'This user already has a chain'
// after
const inFlight = new Map();
function claimOnce(owner) {
if (!inFlight.has(owner)) inFlight.set(owner, faucet.claim(owner).finally(() => inFlight.delete(owner)));
return inFlight.get(owner);
} Defensive patterns
Strategy: fallback
Validate before calling
const existing = await faucet.queryChainForOwner(owner); // if exposed if (existing) return existing; // skip claim entirely
Try / catch
try { return await faucet.claim(owner); } catch (e) { if (/already has a chain/i.test(e.message)) return await faucet.claim(owner); /* front door now returns the existing description */ throw e; } Prevention
- Keep a single in-flight claim per owner (dedup map/promise cache).
- Make claim idempotent client-side by checking local state before retrying.
- Never fire parallel claims for the same key from multiple tabs/workers.
When it happens
Trigger: Two concurrent `claim` mutations for the same AccountOwner submitted before the first one is recorded in the faucet database; retrying a claim whose response was lost after the chain was already created and stored.
Common situations: Double-submitted forms; client retry logic firing while the first request is still queued in the faucet batch processor; load tests reusing one owner key with parallel requests.
Related errors
- no admin chain (Root(0)) in genesis config
- This user has no chain yet
- Request processing was cancelled
- No chain created for owner {}
- Query {argument:?} is invalid and could not be deserialized
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/e723b7fd10f664af.
Report an issue: GitHub.