spacedriveapp/spacedrive · error

Worker channel closed trying to cancel a not running task

Error message

Worker channel closed trying to cancel a not running task

What it means

Worker::cancel_not_running_task sends a CancelNotRunningTask control message to the worker's bounded(8) command channel and panics via .expect() if the channel is closed. The channel closes only when the worker's message loop task has terminated and dropped msgs_rx, i.e. the worker gracefully shut down or its task was dropped by runtime teardown. The panic therefore indicates a cancel request was routed to a worker that can no longer process it.

Source

Thrown at crates/task-system/src/worker/mod.rs:167

		&self,
		task_id: TaskId,
		ack: oneshot::Sender<Result<(), SystemError>>,
	) {
		self.msgs_tx
			.send(WorkerMessage::PauseNotRunningTask { task_id, ack })
			.await
			.expect("Worker channel closed trying to pause a not running task");
	}

	pub async fn cancel_not_running_task(
		&self,
		task_id: TaskId,
		ack: oneshot::Sender<Result<(), SystemError>>,
	) {
		self.msgs_tx
			.send(WorkerMessage::CancelNotRunningTask { task_id, ack })
			.await
			.expect("Worker channel closed trying to cancel a not running task");
	}

	pub async fn force_task_abortion(
		&self,
		task_id: TaskId,
		ack: oneshot::Sender<Result<(), SystemError>>,
	) {
		self.msgs_tx
			.send(WorkerMessage::ForceAbortion { task_id, ack })
			.await
			.expect("Worker channel closed trying to force task abortion");
	}

	#[instrument(skip(self), fields(worker_id = self.id))]
	pub async fn shutdown(&self) {
		if let Some(handle) = self
			.handle
			.try_borrow_mut()

View on GitHub (pinned to 6dfeccf211)

Solutions

  1. Guarantee ordering: once system.shutdown() starts, issue no more cancels; await shutdown before dropping or reusing any task handles
  2. Prefer SystemHandle dispatch APIs (they check has_shutdown at system.rs:629/653) over holding Worker handles directly
  3. Library-level fix: treat SendError as 'worker gone' - warn and drop the request (a dead worker already cancels everything it owned via runner.shutdown in runner.rs:572-669) instead of .expect
  4. If you must call during teardown, wrap the call with AssertUnwindSafe(..).catch_unwind() and treat a panic as 'already cancelled/shutdown'

Example fix

// before (worker/mod.rs:164-167)
self.msgs_tx
    .send(WorkerMessage::CancelNotRunningTask { task_id, ack })
    .await
    .expect("Worker channel closed trying to cancel a not running task");

// after
if self
    .msgs_tx
    .send(WorkerMessage::CancelNotRunningTask { task_id, ack })
    .await
    .is_err()
{
    warn!(%task_id, worker_id = self.id, "Worker channel closed; cancel request dropped (worker already shutdown)");
}
Defensive patterns

Strategy: validation

Validate before calling

// Same gate as for pause: block cancels once shutdown has started.
use std::sync::atomic::{AtomicBool, Ordering};

static SHUTTING_DOWN: AtomicBool = AtomicBool::new(false);

fn can_control_tasks() -> bool { !SHUTTING_DOWN.load(Ordering::Acquire) }

// if can_control_tasks() { system_comm.cancel_not_running_task(...).await }

Try / catch

use futures::FutureExt;
use std::panic::AssertUnwindSafe;

if AssertUnwindSafe(handle.cancel()).catch_unwind().await.is_err() {
    // Cancel raced worker shutdown; shutdown finalizes/cancels everything
    // the dead worker owned, so there is nothing left to cancel.
}

Prevention

When it happens

Trigger: The cancel chain TaskRemoteController (task.rs:482) -> system dispatch (system.rs:349/367) -> Worker::cancel_not_running_task (worker/mod.rs:159) racing shutdown: the has_shutdown gate is checked before dispatch, but a cancel in flight when the target worker's run loop returns (ShutdownRequest handling in run.rs:90) finds a closed channel. Also fires when the tokio runtime drops the worker task mid-dispatch.

Common situations: Canceling tasks from UI/event handlers during daemon shutdown; cancel issued from a task's own Drop while the system is already shutting down; test harnesses that drop the runtime before cancels complete; refactors that moved cancel calls after an await on system.shutdown().

Related errors


AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16). Data as JSON: /api/errors/3b3289d1c84963f8. Report an issue: GitHub.