{"record":{"id":"3315956f9486297f","repo":"FuelLabs/fuel-core","slug":"the-block-height-subscription-channel-was-closed","errorCode":null,"errorMessage":"The block height subscription channel was closed: {:?}","messagePattern":"The block height subscription channel was closed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/fuel-core/src/graphql_api/block_height_subscription.rs","lineNumber":76,"sourceCode":"    ) -> anyhow::Result<()> {\n        let future = {\n            let mut inner_map = self.inner.write();\n\n            if inner_map.current_block_height >= block_height {\n                return Ok(());\n            }\n\n            let (tx, rx) = oneshot::channel();\n            let handlers = inner_map\n                .tx_handles\n                .entry(Reverse(block_height))\n                .or_default();\n            handlers.push(tx);\n            rx\n        };\n\n        future.await.map_err(|e| {\n            anyhow::anyhow!(\"The block height subscription channel was closed: {:?}\", e)\n        })\n    }\n\n    pub fn current_block_height(&self) -> BlockHeight {\n        self.inner.read().current_block_height\n    }\n}\n\n#[derive(Debug, Default)]\nstruct HandlersMapInner {\n    tx_handles: BTreeMap<Reverse<BlockHeight>, Vec<oneshot::Sender<()>>>,\n    current_block_height: BlockHeight,\n}\n\nimpl HandlersMapInner {\n    fn new(current_block_height: BlockHeight) -> Self {\n        Self {\n            tx_handles: BTreeMap::new(),","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/FuelLabs/fuel-core/blob/b9d4d170da3a31c9ace5f963d633b326348e0d42/crates/fuel-core/src/graphql_api/block_height_subscription.rs#L58-L94","documentation":"Subscriber::wait_for_block_height registers a oneshot channel in a BTreeMap keyed by Reverse(block_height) and awaits it. The future resolves with Err (channel closed) when every stored Sender is dropped without sending — the sender lives in the subscription service's handler map, so closure means the service dropped the handlers (e.g., shutdown, cleanup, or map replacement) before the height was reached.","triggerScenarios":"Awaiting a block height while the BlockHeightSubscriptionService is stopped/dropped or clears its handler map — node shutdown, service restart, or internal cleanup removing pending handlers.","commonSituations":"GraphQL subscriptions or internal awaiters pending on future heights during node shutdown or service re-initialization; long waits for heights that are never produced (paused block production) followed by cleanup.","solutions":["Retry the wait after confirming the node is still running and producing/importing blocks.","Before waiting, short-circuit if current_block_height() already satisfies the target.","Treat the error as transient and re-subscribe rather than as data corruption."],"exampleFix":"// before\nsubscriber.wait_for_block_height(h).await?;\n\n// after\nif subscriber.current_block_height() >= h {\n    return Ok(());\n}\nsubscriber.wait_for_block_height(h).await?;","handlingStrategy":"retry","validationCode":"// Fast-path when the height is already reached; the channel only closes on service teardown.\nif subscriber.current_block_height() >= wanted_height {\n    return Ok(());\n}\nsubscriber.wait_for_block_height(wanted_height).await?;","typeGuard":null,"tryCatchPattern":"loop {\n    if subscriber.current_block_height() >= wanted { break; }\n    match subscriber.wait_for_block_height(wanted).await {\n        Ok(()) => break,\n        Err(e) if e.to_string().contains(\"channel was closed\") => {\n            // transient teardown of the subscription service: back off and retry\n            tokio::time::sleep(Duration::from_millis(500)).await;\n            continue;\n        }\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Always short-circuit on current_block_height() before awaiting a future height.","Wrap height waits in an overall timeout so service teardown cannot hang callers.","Treat channel-closure as transient; re-subscribe instead of failing the request."],"tags":["graphql","subscription","oneshot-channel","shutdown","rust"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}