Hmbown/CodeWhale · error

Task store is busy; state is unavailable

Error message

Task store is busy; state is unavailable

What it means

Acquiring the runtime process owner file lock for the task store timed out after 5 seconds of 5ms retries. Another process currently holds the lock, so the manager cannot access task state safely and bails instead of corrupting shared state.

Solutions

  1. Close the other process holding the task store, then retry the operation
  2. Remove a stale lock file only after confirming no live process owns it, then retry
  3. Point this instance at a different task-store path to avoid contention
  4. Investigate why try_acquire_file fails persistently (permissions, network filesystem) if no competing process exists

Example fix

// before
let owner = acquire_owner_lock_with_deadline(&path).await?; // bails after 5s
// after
let owner = match acquire_owner_lock_with_deadline(&path).await {
    Ok(o) => o,
    Err(e) => {
        eprintln!("task store busy: {e}; retrying once after other process exits");
        tokio::time::sleep(Duration::from_secs(2)).await;
        acquire_owner_lock_with_deadline(&path).await?
    }
};
Defensive patterns

Strategy: retry

Validate before calling

// No pre-check is reliable; probe instead:
if RuntimeProcessOwnerLock::try_acquire_file(&path, false)?.is_none() {
    // store currently busy; defer or warn user
}

Try / catch

match acquire_owner_lock(&path).await {
    Err(e) if e.to_string().contains("Task store is busy") => {
        tokio::time::sleep(Duration::from_secs(2)).await;
        acquire_owner_lock(&path).await? // bounded retry
    }
    r => r,
}

Prevention

When it happens

Trigger: RuntimeProcessOwnerLock::try_acquire_file keeps failing for 5s because another Codewhale/CLI process holds the task-store owner lock; or the lock file cannot be acquired due to filesystem issues (stale lock, NFS, permissions).

Common situations: Two CLI/TUI instances open on the same task store concurrently; a crashed process left a lock that the OS never released (stale lock file semantics); the store lives on a filesystem where try-acquire misbehaves (network mounts); heavy retry storms saturating the lock.

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 Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/d7f0e8afa104811c. Report an issue: GitHub.

Appendix: source

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

                    ),
                    detail_path: None,
                },
            );
            task.github_events.push(event);
        }

        Ok(())
    }

    async fn lock_store(&self) -> Result<RuntimeProcessOwnerLock> {
        let path = self.cfg.data_dir.join("task-store.lock");
        let deadline = Instant::now() + Duration::from_secs(5);
        loop {
            if let Some(owner) = RuntimeProcessOwnerLock::try_acquire_file(&path, true)? {
                return Ok(owner);
            }
            if Instant::now() >= deadline {
                bail!("Task store is busy; state is unavailable");
            }
            sleep(Duration::from_millis(5)).await;
        }
    }

    fn refresh_locked(&self, state: &mut ManagerState) -> Result<()> {
        let loaded = load_state(&self.tasks_dir, &self.queue_path)?;
        state.tasks = loaded.tasks;
        state.queue = loaded.queue;
        for (id, events) in &state.pending_events {
            let task = state
                .tasks
                .get_mut(id)
                .context("Pending task disappeared")?;
            self.require_execution_owner(task)?;
            for event in events {
                self.apply_event_to_task(task, event.clone())?;
            }

View on GitHub (pinned to 433685b202)