{"record":{"id":"ed37cafd95a5f770","repo":"linera-io/linera-protocol","slug":"chain-listener-exited-unexpectedly-result","errorCode":null,"errorMessage":"Chain listener exited unexpectedly: {result:?}","messagePattern":"Chain listener exited unexpectedly: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"linera-bridge/src/relay/mod.rs","lineNumber":505,"sourceCode":"        %bridge_app_id,\n        %fungible_app_id,\n        \"Relay is ready\"\n    );\n\n    // Safety-net inbox drain. Notification delivery can be missed (relay down,\n    // stream hiccup), and a missed `NewIncomingBundle` would otherwise strand\n    // its messages until the next one. Reuse the monitor's scan interval as the\n    // drain cadence; an empty inbox makes `process_inbox` a cheap no-op.\n    let mut inbox_drain_interval = tokio::time::interval(monitor_scan_interval);\n    // Consume the immediate first tick — the startup drain above already ran.\n    inbox_drain_interval.tick().await;\n\n    // ── Main loop: process chain operations + notifications ──\n    tracing::info!(\"Listening for chain operations and notifications...\");\n    loop {\n        tokio::select! {\n            result = &mut chain_listener_handle => {\n                anyhow::bail!(\"Chain listener exited unexpectedly: {result:?}\");\n            }\n            result = &mut evm_scan_handle => {\n                anyhow::bail!(\"EVM scan loop exited unexpectedly: {result:?}\");\n            }\n            result = &mut linera_scan_handle => {\n                anyhow::bail!(\"Linera scan loop exited unexpectedly: {result:?}\");\n            }\n            result = &mut retry_handle => {\n                anyhow::bail!(\"Retry loop exited unexpectedly: {result:?}\");\n            }\n            result = &mut http_server_handle => {\n                anyhow::bail!(\"HTTP server exited unexpectedly: {result:?}\");\n            }\n            result = &mut admin_server_handle => {\n                anyhow::bail!(\"Admin HTTP server exited unexpectedly: {result:?}\");\n            }\n            _ = inbox_drain_interval.tick() => {\n                // Periodic safety net for a missed `NewIncomingBundle`: sync and","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/src/relay/mod.rs#L487-L523","documentation":"The relay's serve_loop supervises six concurrent tasks with tokio::select!. The chain-listener task (which subscribes to notifications from validators) terminated — normally or by panic — and the relay deliberately fails fast: any supervised task exiting brings down the whole serve loop, because the relay cannot do its job with a dead notification stream.","triggerScenarios":"The chain listener future returns (notification stream ended, client shut down) or panics (bug in notification handling); validator connections all drop in a way that ends the stream; the wallet/client inside the listener hits a fatal error. 'result' in the message is Result<(), JoinError>-shaped: Ok means clean exit, Err(JoinError) means panic/abort.","commonSituations":"Validators restarting or being unavailable long enough that the notification stream closes; a panic in a code path only reachable with specific notifications; client key/permission errors killing the listener at startup.","solutions":["Read the relay logs immediately before this line — the actual cause (panic backtrace or stream-end warning) is logged by the dying task or an earlier warn.","If it is Err(JoinError) with is_panic(), fix the panic using the backtrace; if it is Ok(()), find why the stream ended (validator downtime, auth).","Restart the relay process (systemd/docker restart policy) — serve_loop is designed to be restarted wholesale.","If validator downtime is routine in your deployment, ensure restarts are automated and monitor relay liveness."],"exampleFix":"// before (serve_loop)\nresult = &mut chain_listener_handle => {\n    anyhow::bail!(\"Chain listener exited unexpectedly: {result:?}\");\n}\n\n// after: enrich with panic/stream-end distinction for operators\nresult = &mut chain_listener_handle => {\n    match result {\n        Ok(()) => anyhow::bail!(\"Chain listener stream ended (validators unreachable?)\"),\n        Err(join) if join.is_panic() => anyhow::bail!(\"Chain listener panicked: {join}\"),\n        Err(join) => anyhow::bail!(\"Chain listener task aborted: {join}\"),\n    }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// serve_loop's error is fatal by design; handle at process level:\n// systemd unit: [Service] Restart=always RestartSec=5\n// in code, the supervisor task can distinguish causes:\nif let Err(join) = &result && join.is_panic() {\n    tracing::error!(backtrace = ?join, \"chain listener panicked\");\n}","preventionTips":["Run the relay under an auto-restarting supervisor.","Alert on relay liveness (admin /health endpoint) so a silent exit is noticed.","Keep validators reachable and authenticated so the notification stream does not end."],"tags":["linera-bridge","relay","task-supervision","tokio","notifications","rust"],"backgroundTag":"background-task-exited","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}