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
- Close the other process holding the task store, then retry the operation
- Remove a stale lock file only after confirming no live process owns it, then retry
- Point this instance at a different task-store path to avoid contention
- 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
- Run a single manager process per task-store path
- Detect and clean stale lock files after crashes
- Avoid network filesystems for task stores
- Back off and retry with jitter under contention
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
- Another pet owner is running
- Cannot open session : its queued input is already open in…
- child wall-time budget exhausted
- Fleet ledger lock was replaced; reopen the workspace before…
- Timed out waiting for task
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)