getzola/zola · error

Couldn't get filename from page asset

Error message

Couldn't get filename from page asset

What it means

Site::copy_assets copies page asset files, computing each asset's destination relative to the parent path via Path::strip_prefix. It panics with this expect when an asset path does not start with the given parent prefix, meaning the asset lies outside the page's content directory.

Source

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

        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"),
                ),
                self.config.hard_link_static,
            )?;
        }

        Ok(())
    }

    /// Deletes the `public` directory (only for `zola build`) and builds the site
    pub fn build(&self) -> Result<()> {
        let mut start = Instant::now();
        // Do not clean on `zola serve` otherwise we end up copying assets all the time
        if self.build_mode == BuildMode::Disk {
            self.clean()?;
        }
        start = log_time(start, "Cleaned folder");

        // Generate/move all assets before markdown any content

View on GitHub (pinned to 61d3082821)

Solutions

  1. Ensure every asset passed with a page lives under that page's directory (same content dir).
  2. Canonicalize both parent and asset paths (fs::canonicalize / std::path::absolute) before the strip so symlinks don't break the prefix.
  3. Check for path separator or case mismatches (Windows vs Unix) between parent and asset.
  4. Copy out-of-page assets via a static/ path instead of page-attached assets.

Example fix

// before
asset_path.strip_prefix(parent).expect("Couldn't get filename from page asset")
// after
let canon_parent = parent.canonicalize()?;
let canon_asset = asset_path.canonicalize()?;
canon_asset.strip_prefix(&canon_parent).expect("asset outside page dir")
Defensive patterns

Strategy: validation

Validate before calling

// Validate every asset is under the page dir before building
for asset in assets {
    let p = std::fs::canonicalize(asset.as_ref())?;
    assert!(p.starts_with(std::fs::canonicalize(parent)?), "asset {:?} outside page dir {:?}", p, parent);
}

Type guard

fn asset_under_parent(asset: &Path, parent: &Path) -> bool {
    let (a, p) = (asset.canonicalize().ok(), parent.canonicalize().ok());
    match (a, p) { (Some(a), Some(p)) => a.starts_with(p), _ => false }
}

Try / catch

std::panic::catch_unwind(|| copy_assets(...)).map_err(|_| anyhow!("asset path outside page directory"))?

Prevention

When it happens

Trigger: Calling site building/copy logic where a page's assets slice contains a path that is not a descendant of parent — e.g. a manually registered asset path outside the .md file's directory, or a normalized/aliased path that diverges from parent.

Common situations: Symlinked content directories where canonicalization changes the prefix; custom page/asset wiring that passes absolute or ../ paths; cross-platform path separator mismatches when parent was built differently from asset_path.

Related errors


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