{"record":{"id":"97f242176be62c31","repo":"linera-io/linera-protocol","slug":"unexpected-response-type","errorCode":null,"errorMessage":"Unexpected response type","messagePattern":"Unexpected response type","errorType":"exception","errorClass":"async_graphql::Error","httpStatus":null,"severity":"error","filePath":"linera-faucet/server/src/lib.rs","lineNumber":544,"sourceCode":"\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),\n            PendingResponse::Daily(_) => Err(Error::new(\"Unexpected response type\")),\n        }\n    }\n\n    async fn do_daily_claim(\n        &self,\n        owner: AccountOwner,\n        destination: AccountOwner,\n    ) -> Result<ClaimOutcome, Error> {\n        // Each early return below is a *refusal*, not a failure, and must be counted\n        // 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();","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-faucet/server/src/lib.rs#L526-L562","documentation":"`do_claim` only ever enqueues initial claims (`target_chain_id: None`), so the batch processor answers with `PendingResponse::Initial`. The match at the end of `do_claim` therefore expects `Initial`; receiving `PendingResponse::Daily` violates that invariant. The error is a defensive guard, not a condition any public input can produce — seeing it means the faucet's internal request/response routing is inconsistent (a regression or a patched build).","triggerScenarios":"Not reachable through the public `claim`/`dailyClaim` API: the response variant is chosen from the same `request.target_chain_id` field that was set at enqueue time (lib.rs:1099-1112). It would take a code change that pairs an initial-claim request with a daily-claim response, or manual corruption of the pending queue, to hit this arm.","commonSituations":"Running a fork or locally modified faucet where the enqueue path or response construction was changed; mixing binaries from different Linera versions after an upgrade; otherwise effectively never seen on unmodified releases.","solutions":["Verify the faucet is an unmodified release via the `version` GraphQL query and redeploy from a clean checkout of the same version","If you maintain a patched faucet, audit `do_claim`'s enqueue (target_chain_id must be None) and `execute_batch`'s response construction (responses keyed off `request.target_chain_id`) so initial claims always answer with `PendingResponse::Initial`","Report the invariant break upstream with the faucet version and logs, since it indicates an internal routing bug"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn is_invariant_violation(msg: &str) -> bool { msg == \"Unexpected response type\" }","tryCatchPattern":"Catch the GraphQL error and match the exact message `Unexpected response type`; do NOT retry — record the faucet version (via the `version` query) and report it as an internal routing bug. Retrying cannot fix an invariant break.","preventionTips":["Pin the faucet to a released version and verify with the `version` GraphQL query after deploys","Review any local patches touching PendingRequest/PendingResponse construction before deploying","Keep faucet server and clients built from the same source revision"],"tags":["rust","faucet","invariant","internal-bug","unreachable","linera"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}