ultraworkers/claw-code · error
registry lock poisoned
Error message
registry lock poisoned
What it means
This panic fires in TaskRegistry::create_task (task_registry.rs:152), the shared implementation behind TaskRegistry::create and create_from_packet. It locks the Arc<Mutex<RegistryInner>> guarding the task HashMap and counter; if any thread ever panicked while holding that mutex, the lock is poisoned and this .expect("registry lock poisoned") panics on every subsequent task creation.
Source
Thrown at rust/crates/runtime/src/task_registry.rs:152
&self,
packet: TaskPacket,
) -> Result<Task, TaskPacketValidationError> {
let packet = validate_packet(packet)?.into_inner();
// Use scope_path as description if available, otherwise use scope as string
let description = packet
.scope_path
.clone()
.or_else(|| Some(packet.scope.to_string()));
Ok(self.create_task(packet.objective.clone(), description, Some(packet)))
}
fn create_task(
&self,
prompt: String,
description: Option<String>,
task_packet: Option<TaskPacket>,
) -> Task {
let mut inner = self.inner.lock().expect("registry lock poisoned");
inner.counter += 1;
let ts = now_secs();
let task_id = format!("task_{:08x}_{}", ts, inner.counter);
let task = Task {
task_id: task_id.clone(),
prompt,
description,
task_packet,
status: TaskStatus::Created,
created_at: ts,
updated_at: ts,
messages: Vec::new(),
output: String::new(),
team_id: None,
heartbeat: None,
};
inner.tasks.insert(task_id, task.clone());
taskView on GitHub (pinned to 08106b0c37)
Solutions
- Enable RUST_BACKTRACE=1 and identify the original panic that held the TaskRegistry lock; fix that (usually an unwrap/expect or index panic inside a registry method or code invoked under the guard).
- Make create_task poisoning-tolerant since RegistryInner (HashMap + u64 counter) is plain data: lock().unwrap_or_else(|p| p.into_inner()).
- Convert panicking paths inside all TaskRegistry methods to Result-returning error handling so no panic can occur while the guard is alive.
- Wrap lane worker entry points in catch_unwind so worker panics never unwind through code holding the registry guard.
Example fix
// before
let mut inner = self.inner.lock().expect("registry lock poisoned");
// after
let mut inner = self.inner.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); Defensive patterns
Strategy: try-catch
Validate before calling
use std::panic::{catch_unwind, AssertUnwindSafe};
// cheap pre-flight probe: if this panics, the registry is already poisoned
let healthy = catch_unwind(AssertUnwindSafe(|| registry.len())).is_ok();
if !healthy {
eprintln!("task registry poisoned — refusing to create task");
} Try / catch
use std::panic::{catch_unwind, AssertUnwindSafe};
let task = catch_unwind(AssertUnwindSafe(|| {
registry.create(prompt.as_str(), description.as_deref())
}))
.unwrap_or_else(|payload| {
eprintln!("task creation failed — registry poisoned: {payload:?}");
std::process::exit(101);
}); Prevention
- Guard lane/worker thread entry points with catch_unwind so panics never hold registry locks.
- Return Result from registry methods instead of panicking; keep guards only over plain map/counter updates.
- In tests, give each test its own TaskRegistry instead of sharing one Arc across threads.
- Set a panic hook that logs thread name and payload to identify the poisoning panic in long-running processes.
When it happens
Trigger: Calling TaskRegistry::create(prompt, description) or create_from_packet(packet) after any thread has panicked while holding the registry's inner mutex (e.g. a lane worker panicking inside update/stop/append_output between lock() and guard drop). Task creation is usually the first registry touch, so this is typically where poisoning first becomes visible.
Common situations: A background lane/worker thread panics while appending output or updating status on the shared registry; afterwards every new sub-agent task creation aborts the host process. In test suites that share a TaskRegistry across #[tokio::test] or threaded tests, one panicking test poisons the registry for the rest.
Related errors
- team registry lock poisoned
- lsp registry lock poisoned
- mcp registry lock poisoned
- cron registry lock poisoned
- worker registry lock poisoned
AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18).
Data as JSON: /api/errors/e3467d44dec4ff38.
Report an issue: GitHub.