getzola/zola · critical
Couldn't lock imageproc (process_images)
Error message
Couldn't lock imageproc (process_images)
What it means
Site::process_images locks the shared imageproc Mutex to prune and process pending image operations, panicking with this expect message if the lock is poisoned. This runs during build() (called by build when rebuilding), so a poisoned lock aborts the whole build.
Source
Thrown at components/site/src/lib.rs:692
copy_directory(
&self.static_path,
&self.output_path,
self.config.hard_link_static,
self.config.ignored_static_globset.as_ref(),
)?;
}
Ok(())
}
pub fn num_img_ops(&self) -> usize {
let imageproc = self.imageproc.lock().expect("Couldn't lock imageproc (num_img_ops)");
imageproc.num_img_ops()
}
pub fn process_images(&self) -> Result<()> {
let mut imageproc =
self.imageproc.lock().expect("Couldn't lock imageproc (process_images)");
imageproc.prune()?;
imageproc.do_process()
}
/// Deletes the `public` directory if it exists and the `preserve_dotfiles_in_output` option is set to false,
/// or if set to true: its contents except for the dotfiles at the root level.
pub fn clean(&self) -> Result<()> {
clean_site_output_folder(&self.output_path, self.config.preserve_dotfiles_in_output)
}
fn copy_assets(&self, parent: &Path, assets: &[impl AsRef<Path>], dest: &Path) -> Result<()> {
for asset in assets {
let asset_path = asset.as_ref();
copy_file_if_needed(
asset_path,
&dest.join(
asset_path.strip_prefix(parent).expect("Couldn't get filename from page asset"),
),View on GitHub (pinned to 61d3082821)
Solutions
- Fix the underlying panic in image processing (the original panic message appears earlier in the log).
- Recover with unwrap_or_else(|p| p.into_inner()) if poisoned state is acceptable.
- Restart the build process/server after an image-processing panic instead of reusing the poisoned Site.
- Add error handling in image operations so workers return Result instead of panicking.
Example fix
// before
let mut imageproc = self.imageproc.lock().expect("Couldn't lock imageproc (process_images)");
// after
let mut imageproc = self.imageproc.lock().unwrap_or_else(|p| p.into_inner()); Defensive patterns
Strategy: retry
Try / catch
// Detect poisoned-lock panic and retry with a fresh process
match std::panic::catch_unwind(|| site.process_images()) {
Ok(res) => res?,
Err(_) => { /* rebuild Site / restart worker, then retry once */ }
} Prevention
- Fix the root-cause panic in image processing; it poisons the lock for every later build.
- Restart live-reload servers after a failed build.
- Keep image operations error-returning instead of panicking under the lock.
- Use into_inner() recovery if poisoned imageproc state is still consistent.
When it happens
Trigger: Calling build/process_images after another thread panicked while holding the imageproc lock (e.g. a template function like resize_image panicked mid-operation).
Common situations: Live-reload/rebuild servers where a first build panicked in image handling and the subsequent rebuild hits the poisoned mutex.
Related errors
- Couldn't lock imageproc (set_base_url)
- Couldn't lock imageproc (num_img_ops)
- result cache lock
- result cache lock
- result cache lock
AI-assisted analysis of getzola/zola@61d3082821 (2026-09-03).
Data as JSON: /api/errors/a74ae84340f398ff.
Report an issue: GitHub.