{"record":{"id":"d2f44ae534988c47","repo":"linera-io/linera-protocol","slug":"notification-subscription-failed-errors","errorCode":null,"errorMessage":"Notification subscription failed: {errors:?}","messagePattern":"Notification subscription failed: (.+?)","errorType":"exception","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"linera-service/src/cli_wrappers/wallet.rs","lineNumber":2079,"sourceCode":"            text == \"{\\\"type\\\":\\\"connection_ack\\\"}\",\n            \"Unexpected response: {text}\"\n        );\n        let query_json = json!({\n          \"id\": \"1\",\n          \"type\": \"start\",\n          \"payload\": {\n            \"query\": query,\n            \"variables\": {},\n            \"operationName\": null\n          }\n        });\n        websocket.send(query_json.to_string().into()).await?;\n        Ok(Box::pin(websocket.map_err(anyhow::Error::from).and_then(\n            |message| async {\n                let text = message.into_text()?;\n                let value: Value = serde_json::from_str(&text).context(\"invalid JSON\")?;\n                if let Some(errors) = value[\"payload\"].get(\"errors\") {\n                    bail!(\"Notification subscription failed: {errors:?}\");\n                }\n                serde_json::from_value(value[\"payload\"][\"data\"][\"notifications\"].clone())\n                    .context(\"Failed to deserialize notification\")\n            },\n        )))\n    }\n\n    /// Subscribes to query results via the `queryResult` GraphQL subscription.\n    pub async fn query_result(\n        &self,\n        name: &str,\n        chain_id: ChainId,\n        application_id: &ApplicationId,\n    ) -> Result<Pin<Box<impl Stream<Item = Result<Value>>>>> {\n        let query = format!(\n            r#\"subscription {{ queryResult(name: \"{name}\", chainId: \"{chain_id}\", applicationId: \"{application_id}\") }}\"#,\n        );\n        let url = format!(\"ws://localhost:{}/ws\", self.port);","sourceCodeStart":2061,"sourceCodeEnd":2097,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-service/src/cli_wrappers/wallet.rs#L2061-L2097","documentation":"Raised by the NodeExt test wrapper in linera-service (cli_wrappers/wallet.rs) when the GraphQL websocket subscription `subscription { notifications(chainId: ...) }` against a node's `ws://localhost:{port}/ws` endpoint returns an `errors` array in a message payload. The websocket handshake and `connection_init`/`connection_ack` succeeded, but the node rejected or failed the `start` of the subscription itself. The error surfaces asynchronously as an `Err` item on the returned notification stream, not from the initial `notifications()` call.","triggerScenarios":"Calling `node.notifications(chain_id)` where the chain ID does not exist, is not tracked by that validator/shard, or is inactive; subscribing while the node process has restarted or is a different process than the one the wrapper's port refers to; running a wrapper built from a linera revision whose GraphQL subscription schema differs from the node binary under test.","commonSituations":"End-to-end tests that subscribe before the chain is assigned to the node's shard; version skew between the test client and the linera-service binary; a node that crashed earlier in the test (leaving the stream to error on the next message); chain IDs copy-pasted or constructed incorrectly in fixtures.","solutions":["Verify the chain is known to this node first with a one-shot `query_node` GraphQL query and confirm the chain ID spelling","Check that the node process behind the wrapper's port is alive and is the expected binary (restart the LocalNodeInstance/fixture if it crashed)","Rebuild the test harness client and the linera-service node binary from the same commit to remove schema skew","Drop the broken stream and re-subscribe with `notifications(chain_id)` — subscriptions are stateful across node restarts"],"exampleFix":"// before\nlet mut stream = node.notifications(chain_id).await?;\nwhile let Some(item) = stream.next().await {\n    let notification = item?; // error arrives here mid-stream\n}\n\n// after\nlet mut stream = node.notifications(chain_id).await?;\nwhile let Some(item) = stream.next().await {\n    let notification = item.context(\"notification stream ended with error\")?;\n}","handlingStrategy":"try-catch","validationCode":"// Rust: confirm the chain is known to the node before opening the subscription\nlet response = node\n    .query_node(&format!(\n        \"query {{ chains {{ chainId }} }}\"\n    ))\n    .await?;\nlet known = response[\"data\"][\"chains\"]\n    .as_array()\n    .map(|chains| {\n        chains\n            .iter()\n            .any(|c| c[\"chainId\"].as_str() == Some(&chain_id.to_string()))\n    })\n    .unwrap_or(false);\nanyhow::ensure!(known, \"chain {chain_id} not known to this node\");","typeGuard":null,"tryCatchPattern":"// Consume the stream defensively: the error arrives as an Err item, not from notifications()\nlet mut stream = node.notifications(chain_id).await?;\nwhile let Some(item) = stream.next().await {\n    let notification = match item {\n        Ok(n) => n,\n        Err(e) if e.to_string().contains(\"Notification subscription failed\") => {\n            // server rejected the subscription: verify chain/node state, then re-subscribe once\n            return Err(e.context(\"node rejected notifications subscription\"));\n        }\n        Err(e) => return Err(e),\n    };\n    // handle notification\n}","preventionTips":["Always match on each stream item's Result — never unwrap — because this error is delivered asynchronously","Probe chain existence with a one-shot GraphQL query before subscribing","Keep the test-harness client and the linera-service node binary on the same commit","After a node restart mid-test, drop and re-create the subscription instead of reusing the stream"],"tags":["graphql","websocket","subscription","linera-node","testing"],"backgroundTag":"graphql-subscription-errors","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}