{"record":{"id":"86f7203b5a95ec8c","repo":"gleam-lang/gleam","slug":"could-not-lock-beam-compiler","errorCode":null,"errorMessage":"could not lock beam_compiler","messagePattern":"could not lock beam_compiler","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler-cli/src/fs.rs","lineNumber":111,"sourceCode":"    beam_compiler: Arc<Mutex<Option<BeamCompilerInstance>>>,\n}\n\nimpl ProjectIO {\n    pub fn new() -> Self {\n        Self {\n            beam_compiler: Arc::new(Mutex::new(None)),\n        }\n    }\n\n    pub fn boxed() -> Box<Self> {\n        Box::new(Self::new())\n    }\n\n    pub(crate) fn initialise_beam_compiler(&self) -> Result<(), Error> {\n        let mut guard = self\n            .beam_compiler\n            .lock()\n            .expect(\"could not lock beam_compiler\");\n        if guard.is_none() {\n            *guard = Some(BeamCompilerInstance::new(self)?);\n        }\n        Ok(())\n    }\n}\n\nimpl FileSystemReader for ProjectIO {\n    fn read(&self, path: &Utf8Path) -> Result<String, Error> {\n        read(path)\n    }\n\n    fn read_bytes(&self, path: &Utf8Path) -> Result<Vec<u8>, Error> {\n        read_bytes(path)\n    }\n\n    fn is_file(&self, path: &Utf8Path) -> bool {\n        path.is_file()","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/gleam-lang/gleam/blob/15b07c783065c92bb016b1b74ed0995a5259850f/compiler-cli/src/fs.rs#L93-L129","documentation":"This is a panic from `Mutex::lock()` inside `initialise_beam_compiler`: the mutex guarding the project's lazy `BeamCompilerInstance` was poisoned because another thread panicked while holding it. The library deliberately aborts rather than continue with a possibly half-initialised compiler. It is almost always a secondary error masking an earlier panic in a parallel build.","triggerScenarios":"Calling `project.initialise_beam_compiler()` (directly or via commands like `gleam compile-package`) while another thread already panicked inside a critical section guarded by `self.beam_compiler` — e.g. a concurrent `compile_beam` call crashed while the compiler instance was being created or used.","commonSituations":"Parallel/actor-based builds that share one `Project` across threads (as gleam's build engine does); a segfaulting or panicking beam compiler subprocess handler from a prior run in the same process; running gleam compilation inside a test harness that spawns many builds concurrently and one of them hits an unrelated bug first.","solutions":["Find and fix the ORIGINAL panic: look earlier in the logs for the panic that poisoned the lock (the real error is upstream of this message).","Avoid sharing the `Project`/`ProjectCompiler` across threads, or serialise access so a panic in one compile cannot poison the lock for others.","If recovery is acceptable, patch the call site to reuse the poisoned guard: `.lock().unwrap_or_else(|p| p.into_inner())` instead of `.expect(...)`.","Run `gleam` with `RUST_BACKTRACE=1` on the failing invocation to identify the thread that first panicked."],"exampleFix":"// before\nlet mut guard = self.beam_compiler.lock().expect(\"could not lock beam_compiler\");\n// after (recover from a poisoned lock instead of panicking)\nlet mut guard = self\n    .beam_compiler\n    .lock()\n    .unwrap_or_else(|poisoned| poisoned.into_inner());","handlingStrategy":"try-catch","validationCode":"// ensure the project isn't contended before initialising\nassert!( Arc::strong_count(&project_shared) <= 1, \"project shared across threads\" );","typeGuard":"fn lock_is_healthy<T>(m: &Mutex<T>) -> bool { !m.is_poisoned() }","tryCatchPattern":"std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| project.initialise_beam_compiler()))\n    .map_err(|_| anyhow!(\"beam compiler lock poisoned; an earlier thread panicked\"))?","preventionTips":["Do not share one Project/ProjectCompiler across multiple threads without synchronisation.","Fix root-cause panics first — this error is always secondary to an earlier panic.","Wrap each compilation task in catch_unwind so one failure cannot poison shared locks.","Run CI builds with RUST_BACKTRACE=1 to surface the original panic quickly."],"tags":["concurrency","mutex-poisoned","panic","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"15b07c783065c92bb016b1b74ed0995a5259850f","analyzedAt":"2026-09-14T11:14:59.388Z","contentChangedAt":"2026-09-14T11:14:59.388Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}