Hmbown/CodeWhale · error

This Runtime already owns a task execution manager

Error message

This Runtime already owns a task execution manager

What it means

retain_task_execution_lease stores the single TaskExecutionLease on the Runtime and bails if one is already present. The Runtime may own at most one task execution manager, so a second retain would silently overwrite or corrupt execution ownership.

Solutions

  1. Ensure retain_task_execution_lease is called only once per Runtime lifetime
  2. Reuse the existing lease instead of attaching a new one
  3. Create a fresh Runtime if you genuinely need a new task execution manager

Example fix

// before
runtime.retain_task_execution_lease(lease_a)?;
runtime.retain_task_execution_lease(lease_b)?; // bails
// after
runtime.retain_task_execution_lease(lease_a)?;
let lease = runtime.task_execution_lease(); // reuse existing
Defensive patterns

Strategy: validation

Validate before calling

// attach once, at construction
once: OnceCell<TaskExecutionLease>

Try / catch

match runtime.retain_task_execution_lease(lease) {
    Err(e) if e.to_string().contains("already owns a task execution manager") => {
        // reuse the existing lease instead
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling retain_task_execution_lease twice on the same Runtime instance (double initialization, or re-attaching a new TaskManager without detaching the old lease).

Common situations: Re-running an init/setup path; a plugin or automation layer attaching its own TaskManager on top of the engine's; accidental duplicate bootstrap during startup retries.

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/79ee0f11c1076544. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:5269

        monitors.push(retained_completion(worker));
    }

    fn ensure_accepting_execution(&self) -> Result<()> {
        if self.cancel_token.is_cancelled() {
            bail!("Runtime is shutting down; execution admission is closed");
        }
        Ok(())
    }

    /// Runtime clones retained by live Engine/tool work retain this lease too.
    /// A TaskManager cancellation timeout cannot release execution ownership.
    pub(crate) fn retain_task_execution_lease(
        &self,
        lease: Arc<crate::task_manager::TaskExecutionLease>,
    ) -> Result<()> {
        let mut current = self.task_execution_lease.lock();
        if current.is_some() {
            bail!("This Runtime already owns a task execution manager");
        }
        *current = Some(lease);
        Ok(())
    }

    /// Attach the automation manager for model-visible scheduling tools.
    pub fn attach_automation_manager(
        &self,
        automations: crate::automation_manager::SharedAutomationManager,
    ) {
        *self.automations.lock() = Some(automations);
    }

    /// Mints an approval identifier that no provider can predict or collide
    /// with. Every `approval_id` an external client ever sees comes from here,
    /// so `approval_id` never carries a provider tool-call ID and a client can
    /// echo it without inspecting where it came from.
    fn mint_approval_id() -> String {

View on GitHub (pinned to 73e0f67d83)