getzola/zola · error

Couldn't lock imageproc (num_img_ops)

Error message

Couldn't lock imageproc (num_img_ops)

What it means

Site::num_img_ops locks the shared imageproc Mutex to report the number of pending image operations, panicking with this expect message if the lock cannot be acquired. As with other imageproc lock sites, this only happens when the mutex is poisoned by a panic in another thread holding it.

Source

Thrown at components/site/src/lib.rs:686

                false,
                None,
            )?;
        }
        // We're fine with missing static folders
        if self.static_path.exists() {
            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 {

View on GitHub (pinned to 61d3082821)

Solutions

  1. Fix the root-cause panic that poisoned the mutex; check earlier log output for the original backtrace.
  2. Use unwrap_or_else(|p| p.into_inner()) to tolerate poisoning when reading the count.
  3. Avoid invoking num_img_ops once a build failure is known.
  4. Serialize access so image-processing panics are handled before the count is queried.

Example fix

// before
let imageproc = self.imageproc.lock().expect("Couldn't lock imageproc (num_img_ops)");
// after
let imageproc = self.imageproc.lock().unwrap_or_else(|p| p.into_inner());
Defensive patterns

Strategy: try-catch

Try / catch

let n = match site.imageproc.lock() {
    Ok(g) => g.num_img_ops(),
    Err(poisoned) => poisoned.into_inner().num_img_ops(),
};

Prevention

When it happens

Trigger: Calling num_img_ops after the imageproc mutex was poisoned by a panic elsewhere (e.g. during process_images or a template-driven image operation).

Common situations: Build tooling that reports image op counts after a partial/failed build in which an image worker panicked.

Related errors


AI-assisted analysis of getzola/zola@61d3082821 (2026-09-03). Data as JSON: /api/errors/28e0fa6c43068eb9. Report an issue: GitHub.