Skip to content

fix(runtimed-py): keep connect runtime alive — fixes kernel launch from Python bindings#994

Merged
rgbkrk merged 1 commit intomainfrom
fix/kernel-launch-pyproject
Mar 20, 2026
Merged

fix(runtimed-py): keep connect runtime alive — fixes kernel launch from Python bindings#994
rgbkrk merged 1 commit intomainfrom
fix/kernel-launch-pyproject

Conversation

@rgbkrk
Copy link
Member

@rgbkrk rgbkrk commented Mar 20, 2026

Fixes #992.

The bug

Every start_kernel() call from the Python bindings failed with RuntimedError: Disconnected from sync task. The daemon log showed Broken pipe (os error 32) during streaming load. The Tauri frontend also couldn't start kernels for saved notebooks.

Root cause

The sync Session methods (open_notebook_with_socket, create_notebook_with_socket, join_notebook_with_socket) all followed this pattern:

let runtime = Runtime::new()?;
let (notebook_id, state, _) = runtime.block_on(connect_open(...))?;
drop(runtime);  // ← KILLS THE SYNC TASK
Self::from_state(notebook_id, state, peer_label)  // creates a NEW runtime

connect_open spawns a sync task via tokio::spawn inside build_and_spawn. That task runs on runtime. When runtime is dropped, the sync task is cancelled. from_state creates a fresh runtime, but the sync task is gone — the DocHandle's command channels are dead.

Why reads worked: get_cells() reads directly from the local Automerge doc (no sync task needed).

Why writes failed: start_kernel() sends LaunchKernel through DocHandle::send_request()cmd_tx.send() → the sync task's cmd_rx is dead → SyncError::Disconnected.

Why the daemon saw broken pipe: The sync task owned the socket reader. When the runtime dropped, the socket closed. The daemon's next send_typed_frame() got EPIPE.

Fix

Pass the connect runtime through to the Session instead of dropping it and creating a new one. New method from_state_with_runtime(runtime, ...) keeps the sync task alive.

Verification

client = runtimed.Client()
session = client.create_notebook(runtime='python')
session.start_kernel(kernel_type='python', env_source='uv:prewarmed')
result = session.run('print("Hello!")')
# success=True → "Hello!"

Also verified with open_notebook on a saved .ipynb file with env_source='auto'.

)

The sync Session methods (open_notebook, create_notebook, join_notebook)
were creating a tokio runtime for the connect call, then dropping it
before creating the Session. This killed the sync task that was spawned
during connect, breaking all subsequent daemon requests (start_kernel,
execute_cell, etc.).

The fix passes the connect runtime through to the Session instead of
dropping it and creating a new one. The sync task stays alive for the
lifetime of the Session.

Root cause: runtime A spawns sync task → drop(runtime A) cancels task →
from_state creates runtime B → DocHandle channels are dead → every
send_request gets SyncError::Disconnected.

Symptoms:
- start_kernel: 'Disconnected from sync task'
- Daemon log: 'Streaming load failed: Broken pipe (os error 32)'
- Frontend: kernel launch silently fails
@rgbkrk rgbkrk force-pushed the fix/kernel-launch-pyproject branch from 441dc47 to 75270ec Compare March 20, 2026 16:51
@rgbkrk rgbkrk merged commit 69f6bb2 into main Mar 20, 2026
16 of 19 checks passed
@rgbkrk rgbkrk deleted the fix/kernel-launch-pyproject branch March 20, 2026 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kernel launch fails with "Disconnected from sync task" — broken pipe during streaming load

1 participant