{"record":{"id":"0854e68fa10de6e0","repo":"linera-io/linera-protocol","slug":"request-processing-was-cancelled","errorCode":null,"errorMessage":"Request processing was cancelled","messagePattern":"Request processing was cancelled","errorType":"exception","errorClass":"async_graphql::Error","httpStatus":null,"severity":"error","filePath":"linera-faucet/server/src/lib.rs","lineNumber":525,"sourceCode":"                daily_period: 0,\n                responder: tx,\n                #[cfg(with_metrics)]\n                queued_at: std::time::Instant::now(),\n            });\n\n            #[cfg(with_metrics)]\n            metrics::QUEUE_SIZE\n                .with_label_values(&[])\n                .observe(requests.len() as f64);\n        }\n\n        // Notify the batch processor that there's a new request.\n        self.request_notifier.notify_one();\n\n        // Wait for the result\n        let response = rx\n            .await\n            .map_err(|_| Error::new(\"Request processing was cancelled\"))?;\n\n        #[cfg(with_metrics)]\n        {\n            // Refusals detected during batch validation (e.g. a duplicate that\n            // raced past the front-door check) arrive here as errors; classify\n            // them by message so they are counted once, under their own label.\n            let label = match &response {\n                PendingResponse::Initial(Ok(_)) => \"success\",\n                PendingResponse::Initial(Err(error)) => refusal_label(error).unwrap_or(\"error\"),\n                PendingResponse::Daily(_) => \"error\",\n            };\n            metrics::CLAIM_REQUESTS_TOTAL\n                .with_label_values(&[label])\n                .inc();\n        }\n\n        match response {\n            PendingResponse::Initial(result) => result.map(|b| *b),","sourceCodeStart":507,"sourceCodeEnd":543,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-faucet/server/src/lib.rs#L507-L543","documentation":"The `claim` GraphQL mutation pushes a PendingRequest carrying a oneshot responder onto the faucet's shared queue and then awaits the reply channel. This error means the batch processor dropped that responder without ever sending a PendingResponse, so `rx.await` returned a oneshot RecvError. In practice the faucet's batch-processing task exited (shutdown, fatal batch error, or an early `?` return in `execute_batch` such as a failed `update_wallet`) while the request was queued or in flight. It is a server-side lifecycle failure, not a problem with the claim arguments.","triggerScenarios":"Calling the `claim` mutation while the faucet server is shutting down (its CancellationToken fired and the final `process_batch` failed); `execute_batch` failing after operations execute but before responses are sent, e.g. `update_wallet` returning an error via `?` or `extract_opened_single_owner_chains` failing (both drop the responders of popped requests); the batch processor task panicking or the process dying between enqueue and reply.","commonSituations":"Faucet redeployments/restarts while test or bot clients are claiming; corrupted or locked faucet wallet making `update_wallet` fail; storage or validator outages surfacing as `Batch processing error` in the logs; test harnesses tearing the service down with claims still queued (the pattern exercised by test_faucet_persistence and test_faucet_rate_limiting).","solutions":["Retry the `claim` after a short backoff — it is safe: if the first attempt actually landed, `do_claim`'s duplicate path (lib.rs:487-494) returns the existing chain description instead of creating a second chain","Check the faucet server logs for `Batch processing error` or `Failed to execute batch` to find the underlying cause that dropped the responder","Verify the faucet's wallet and storage backends are reachable and consistent — a failing `update_wallet` after `execute_operations` drops responders (lib.rs:958-962)","If every claim fails this way, restart the faucet service and investigate why the batch loop is not completing"],"exampleFix":"// before\nlet description = client.claim(owner, None).await?;\n\n// after - treat cancellation as transient; re-claiming is safe\nlet description = loop {\n    match client.claim(owner, None).await {\n        Ok(description) => break description,\n        Err(e) if e.message.contains(\"Request processing was cancelled\") => {\n            tokio::time::sleep(std::time::Duration::from_secs(2)).await;\n        }\n        Err(e) => return Err(e),\n    }\n};","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"In your GraphQL client, catch the mutation error and match its message; on `Request processing was cancelled` sleep 2-5s (with jitter) and re-issue the same `claim(owner)` — the mutation is idempotent for owners that already hold a chain (the duplicate path returns the existing description). Give up after a few attempts and surface a 'faucet unavailable' state.","preventionTips":["Treat `Request processing was cancelled` as transient: retry with backoff instead of surfacing it to users","Pause claim traffic during planned faucet restarts/deployments and drain in-flight requests","Watch the faucet's `claim_requests_total{result=\"error\"}` and `batch_processing_latency_ms{result=\"error\"}` metrics — sustained errors precede dropped responders","Keep the faucet wallet healthy; a failing `update_wallet` drops responders after the block executes"],"tags":["rust","graphql","faucet","oneshot-channel","shutdown","transient","linera"],"backgroundTag":"background-task-channel-closed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}