{"record":{"id":"f31ba67b5bf75e46","repo":"tracel-ai/burn","slug":"service-call-failed","errorCode":null,"errorMessage":"Service call failed","messagePattern":"Service call failed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/burn-remote/src/client/base.rs","lineNumber":32,"sourceCode":"impl RemoteClient {\n    pub fn init(device: RemoteDevice) -> Self {\n        // `DeviceHandle::new` initializes the service the first time it's called for a given\n        // device id. `RemoteService::init` is deliberately cheap — it records the endpoint but\n        // does NOT connect, because cubecl holds a process-global lock across it; the actual\n        // connect + handshake happens lazily on first use (or via `ensure_connected`).\n        // Subsequent calls return a handle to the existing service.\n        let handle = DeviceHandle::<RemoteService>::new(device.to_id());\n        Self { device, handle }\n    }\n\n    /// Force the lazily-established connection to be opened now, populating the device's\n    /// settings/device-count cells. Used by the settings path (`RemoteDevice::defaults` /\n    /// `enumerate`), which needs the handshake reply before any op has flushed. Runs the\n    /// connect on the service's runner thread, so it can't sit under cubecl's global lock.\n    pub(crate) fn ensure_connected(&self) {\n        self.handle\n            .submit_blocking(|s| s.ensure_connected())\n            .expect(\"Service call failed\");\n    }\n\n    /// Establish the session asynchronously, the way the browser requires.\n    ///\n    /// The connect + handshake cannot block the single browser thread, so it runs off the device\n    /// handle: the service hands back the connection parameters, the network round-trip happens\n    /// with `.await`, and the opened session is installed back into the service. A no-op once the\n    /// session is up.\n    #[cfg(target_family = \"wasm\")]\n    pub(crate) async fn connect_async(&self) {\n        use crate::client::service::wasm_connect;\n\n        let Some(plan) = self\n            .handle\n            .submit_blocking(|s| s.wasm_connect_plan())\n            .expect(\"Service call failed\")\n        else {\n            return;","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/tracel-ai/burn/blob/d16f7ba2ed0d41408189384044cc886fb4c8f957/crates/burn-remote/src/client/base.rs#L14-L50","documentation":"`RemoteClient::ensure_connected` submits a blocking connect/handshake task to the remote service's runner thread via `handle.submit_blocking(...)` and unwraps the returned Option with `expect(\"Service call failed\")`. The service call channel returns `None` when the service/runner is shut down or the submission fails (channel closed), so the unwrap panics. It signals that the background service backing this client is no longer alive.","triggerScenarios":"Calling `ensure_connected()` (directly or via `RemoteDevice::defaults`/`enumerate`) after the service runner thread has stopped (client dropped/shutdown, panic in the service, or the handle's channel was closed), so `submit_blocking` yields `None`.","commonSituations":"Using a remote client/handle after the runtime was torn down; server process crashed mid-session; holding a cloned handle past shutdown; environment where the compute server failed to start (bad address, missing backend).","solutions":["Recreate the client/service connection instead of reusing the stale handle.","Check the remote server is running and reachable before creating the client.","Keep the service/runner alive for the lifetime of all handles (don't drop the owner early).","Inspect server logs for the underlying connection or backend failure."],"exampleFix":"// before\nlet client = RemoteClient::create(&config);\ndrop(runner); // service shut down\nclient.ensure_connected(); // panics: Service call failed\n// after\nlet client = RemoteClient::create(&config);\nclient.ensure_connected(); // while service is alive\n// if shutdown happened: create a fresh client instead of reusing","handlingStrategy":"try-catch","validationCode":"// check liveness before calling\nif client.service_alive() /* or try a cheap submit that tolerates None */ {\n    client.ensure_connected();\n}","typeGuard":null,"tryCatchPattern":"// wrap the panic boundary so the app can reconnect\nlet connected = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| client.ensure_connected()));\nif connected.is_err() {\n    let client = RemoteClient::create(&config); // rebuild service and retry\n    client.ensure_connected();\n}","preventionTips":["Keep the service/runner alive for the lifetime of all handles","Never use client handles after shutdown/drop of the owning runtime","Monitor remote server health before issuing device enumeration","Catch panics at the connection boundary to enable reconnect logic"],"tags":["rust","panic","remote","ipc","shutdown"],"backgroundTag":"service-channel-closed","analyzedSha":"d16f7ba2ed0d41408189384044cc886fb4c8f957","analyzedAt":"2026-09-05T13:19:14.260Z","contentChangedAt":"2026-09-05T13:19:14.260Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}