{"record":{"id":"1569db791faad0d0","repo":"ultraworkers/claw-code","slug":"team-registry-lock-poisoned","errorCode":null,"errorMessage":"team registry lock poisoned","messagePattern":"team registry lock poisoned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rust/crates/runtime/src/team_cron_registry.rs","lineNumber":68,"sourceCode":"#[derive(Debug, Clone, Default)]\npub struct TeamRegistry {\n    inner: Arc<Mutex<TeamInner>>,\n}\n\n#[derive(Debug, Default)]\nstruct TeamInner {\n    teams: HashMap<String, Team>,\n    counter: u64,\n}\n\nimpl TeamRegistry {\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    pub fn create(&self, name: &str, task_ids: Vec<String>) -> Team {\n        let mut inner = self.inner.lock().expect(\"team registry lock poisoned\");\n        inner.counter += 1;\n        let ts = now_secs();\n        let team_id = format!(\"team_{:08x}_{}\", ts, inner.counter);\n        let team = Team {\n            team_id: team_id.clone(),\n            name: name.to_owned(),\n            task_ids,\n            status: TeamStatus::Created,\n            created_at: ts,\n            updated_at: ts,\n        };\n        inner.teams.insert(team_id, team.clone());\n        team\n    }\n\n    pub fn get(&self, team_id: &str) -> Option<Team> {\n        let inner = self.inner.lock().expect(\"team registry lock poisoned\");\n        inner.teams.get(team_id).cloned()","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/ultraworkers/claw-code/blob/08106b0c3771ef5b4a5aa176acccd460e88b7325/rust/crates/runtime/src/team_cron_registry.rs#L50-L86","documentation":"This panic fires in TeamRegistry::create (team_cron_registry.rs:68), which allocates a team_{timestamp}_{counter} id and inserts a new Team into the shared Arc<Mutex<TeamInner>> (HashMap + counter). The expect fires when that mutex is poisoned — some thread previously panicked while holding it — making every subsequent team creation panic.","triggerScenarios":"Calling TeamRegistry::create(name, task_ids) after any thread panicked while holding the TeamRegistry inner mutex (in create, delete, remove, or any other TeamRegistry method on the shared instance).","commonSituations":"Team/cron orchestration where a worker thread panicked during team mutation; afterwards every attempt to group tasks into a new team aborts the CLI process. Also seen in threaded tests sharing one TeamRegistry when a prior test panicked.","solutions":["Reproduce with RUST_BACKTRACE=full and fix the first panic that held the TeamRegistry lock; create() is downstream of it.","Recover the guard — TeamInner is a plain HashMap plus u64 counter: lock().unwrap_or_else(|p| p.into_inner()).","Remove panicking constructs from all TeamRegistry critical sections (use Result-returning error paths).","Wrap orchestration worker entry points in catch_unwind so panics cannot unwind through team mutations."],"exampleFix":"// before\nlet mut inner = self.inner.lock().expect(\"team registry lock poisoned\");\n\n// after\nlet mut inner = self.inner.lock().unwrap_or_else(|poisoned| poisoned.into_inner());","handlingStrategy":"try-catch","validationCode":"use std::panic::{catch_unwind, AssertUnwindSafe};\n\n// pre-flight: verify the team registry is usable before orchestration\nlet healthy = catch_unwind(AssertUnwindSafe(|| !registry.list().is_empty() || true)).is_ok();\nif !healthy {\n    eprintln!(\"team registry poisoned — aborting team creation\");\n}","typeGuard":null,"tryCatchPattern":"use std::panic::{catch_unwind, AssertUnwindSafe};\n\nlet team = catch_unwind(AssertUnwindSafe(|| registry.create(name, task_ids)))\n    .unwrap_or_else(|payload| {\n        eprintln!(\"team creation failed — registry poisoned: {payload:?}\");\n        std::process::exit(101);\n    });","preventionTips":["Wrap orchestration workers in catch_unwind before they touch shared team state.","Give each integration test an isolated TeamRegistry; a panicking test must not poison shared state.","Keep create() limited to map/counter updates — move validation and formatting outside the guard."],"tags":["rust","mutex","panic","concurrency","lock-poisoning","team-registry"],"backgroundTag":"mutex-poisoned","analyzedSha":"08106b0c3771ef5b4a5aa176acccd460e88b7325","analyzedAt":"2026-08-18T00:29:38.590Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}