github/copilot-sdk · warning

runtime.shutdown timed out during Client::stop

Error message

runtime.shutdown timed out during Client::stop

What it means

During Client::stop, the code awaits runtime.shutdown() with a bounded timeout (RUNTIME_SHUTDOWN_TIMEOUT). If the async runtime does not finish shutting down in time, the library raises TimedOut and records it among shutdown errors rather than hanging forever.

Solutions

  1. Call Client::stop from outside the client's runtime (or a separate thread/runtime) to avoid self-deadlock
  2. Cancel/drop active streams, subscriptions, and pending requests before stop
  3. Increase or make configurable the shutdown timeout if teardown is legitimately slow
  4. Log the pushed shutdown errors to find which task refused to finish

Example fix

// before
client.stop().await?; // called from a task on client's runtime -> timeout
// after
let handle = tokio::spawn(async move { client.stop().await });
let _ = tokio::time::timeout(Duration::from_secs(10), handle).await;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: call stop from outside the client's runtime
let not_inside_runtime = std::thread::current().name().is_none(); // or spawn stop on a dedicated thread

Try / catch

// Rust
if let Err(e) = client.stop().await {
    tracing::warn!("stop incomplete: {e}; dropping client anyway");
}

Prevention

When it happens

Trigger: Calling Client::stop while background tasks (writer actor, pending RPC handlers, leaked subscriptions) block runtime shutdown past the timeout, or awaiting shutdown from within a task on the same runtime.

Common situations: Calling stop from inside code running on the client's own runtime; open streams/handlers keeping tasks alive; a hung transport read preventing task completion; slow test teardown.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/21a2da4aaa318c65. Report an issue: GitHub.

Appendix: source

Thrown at rust/src/lib.rs:2873

            match tokio::time::timeout(RUNTIME_SHUTDOWN_TIMEOUT, self.rpc().runtime().shutdown())
                .await
            {
                Ok(Ok(())) => {
                    debug!(
                        elapsed_ms = runtime_shutdown_start.elapsed().as_millis(),
                        "Client::stop runtime shutdown complete"
                    );
                }
                Ok(Err(e)) => {
                    warn!(
                        elapsed_ms = runtime_shutdown_start.elapsed().as_millis(),
                        error = %e,
                        "runtime.shutdown failed during Client::stop",
                    );
                    errors.push(e);
                }
                Err(_) => {
                    let e = std::io::Error::new(
                        std::io::ErrorKind::TimedOut,
                        "runtime.shutdown timed out during Client::stop",
                    );
                    warn!(
                        elapsed_ms = runtime_shutdown_start.elapsed().as_millis(),
                        timeout = ?RUNTIME_SHUTDOWN_TIMEOUT,
                        error = %e,
                        "runtime.shutdown timed out during Client::stop",
                    );
                    errors.push(e.into());
                }
            }
        }

        let child = self.inner.child.lock().take();
        let process_tree = self.inner.process_tree.lock().take();
        *self.inner.state.lock() = ConnectionState::Disconnected;
        *self.inner.models_cache.lock() = Arc::new(tokio::sync::OnceCell::new());

View on GitHub (pinned to cd8cf15dc3)