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());
        task

View on GitHub (pinned to 08106b0c37)

Solutions

  1. 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).
  2. Make create_task poisoning-tolerant since RegistryInner (HashMap + u64 counter) is plain data: lock().unwrap_or_else(|p| p.into_inner()).
  3. Convert panicking paths inside all TaskRegistry methods to Result-returning error handling so no panic can occur while the guard is alive.
  4. 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

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


AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18). Data as JSON: /api/errors/e3467d44dec4ff38. Report an issue: GitHub.