Hmbown/CodeWhale · error

Task manager is shutting down; admission is closed

Error message

Task manager is shutting down; admission is closed

What it means

admit_task_record takes the state lock and a cross-process store transaction, then checks the shutdown cancel token. If the task manager is shutting down, admission is closed: no new (or recovered) task records may be added, because no worker will claim them. This fails loud instead of queueing a task that would never run.

Solutions

  1. Check the manager's shutdown state (or hold a shutdown broadcast/shutdown_requires flag) before submitting tasks
  2. Re-submit the task after restart, or persist the intent externally and replay on next startup
  3. Ensure automation schedulers are stopped before initiating task-manager shutdown
  4. Handle the error in callers by surfacing 'app is shutting down' rather than retrying immediately

Example fix

// before
mgr.add_task_with_id(req, id).await?;
// after
if mgr.is_shutdown().await {
    persist_pending_submission(&req).await?; // replay on next startup
} else {
    mgr.add_task_with_id(req, id).await?;
}
Defensive patterns

Strategy: try-catch

Try / catch

match mgr.add_task_with_id(req, id).await { Err(e) if e.to_string().contains("shutting down") => defer_submission_until_restart(req), r => r?, }

Prevention

When it happens

Trigger: Calling add_task_with_id / admit_task_record (or the staged-recovery path that calls it) after task manager shutdown has begun (cancel_token cancelled) — e.g. a task submitted during app exit or while the Runtime is being torn down.

Common situations: A UI/automation race where a user clicks 'run' while the app is quitting; scheduled automation firing during shutdown; recovery replay racing process termination.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/58b42ba13a57f32d. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/task_manager.rs:1771

            tool_calls: Vec::new(),
            timeline: vec![TaskTimelineEntry {
                timestamp: Utc::now(),
                kind: "queued".to_string(),
                summary: "Task queued".to_string(),
                detail_path: None,
            }],
        };

        self.admit_task_record(task, false).await
    }

    async fn admit_task_record(&self, task: TaskRecord, recover_stage: bool) -> Result<TaskRecord> {
        {
            let mut state = self.state.lock().await;
            let _transaction = self.lock_store().await?;
            self.refresh_locked(&mut state)?;
            if self.cancel_token.is_cancelled() {
                bail!("Task manager is shutting down; admission is closed");
            }
            if task.execution_scope.as_deref() != Some(self.execution_scope()) {
                bail!(
                    "Task execution scope is unverified or belongs to another Runtime; refusing adoption"
                );
            }
            let task_path = self.tasks_dir.join(format!("{}.json", task.id));
            // The staged extension is intentionally not `.json`, so startup
            // replay ignores an interrupted create until the queue write has
            // succeeded and this file is atomically promoted.
            let staged_task_path = self.tasks_dir.join(format!(".{}.json.pending", task.id));
            if recover_stage {
                if let Some(accepted) = self.read_bound_task(&task.id)? {
                    validate_bound_task_request(&accepted, &NewTaskRequest::from_task(&task))?;
                    return Ok(accepted);
                }
                let current = read_bound_task_file(&staged_task_path, &task.id)?
                    .context("Unaccepted task stage disappeared during recovery")?;

View on GitHub (pinned to 73e0f67d83)