loco-rs/loco · error · std::io::Error

Failed to retrieve file stem

Error message

Failed to retrieve file stem

What it means

An `io::Error` with `InvalidInput` returned by `strip_template_extension` when the given path has no file stem (no final path component usable as a stem). `Path::file_stem()` returns `None` for paths ending in `..`, empty paths, or root-only paths, and this maps that to an error.

Solutions

  1. Ensure the path passed in has a real file name with a template extension (e.g. `file.t` or `file.html.t`)
  2. Validate the path with `path.file_stem().is_some()` before calling
  3. Guard the caller to skip non-file paths like directories or `.`/`..` components

Example fix

// before
generator.strip_template_extension(Path::new(""))?;
// after
let p = Path::new("templates/user.html.t");
assert!(p.file_stem().is_some());
generator.strip_template_extension(p)?;
Defensive patterns

Strategy: type-guard

Validate before calling

fn has_stem(p: &Path) -> bool { p.file_stem().is_some() && p.extension().is_some() }

Type guard

fn is_strippable(p: &Path) -> bool {
    p.file_stem().is_some() && p.to_string_lossy().ends_with(".t")
}

Try / catch

match generator.strip_template_extension(&path) {
    Ok(stem) => use(stem),
    Err(e) => tracing::warn!("skipping non-template path: {e}"),
}

Prevention

When it happens

Trigger: Calling `strip_template_extension` on a path without a template extension or with no filename component (e.g. `Path::new(""), Path::new("."), Path::new("/"), Path::new("foo/..")`).

Common situations: Programmatic path construction that produced an empty or dotted path before rendering templates; wrong glob/extraction producing paths with no stem.

Related errors


AI-assisted analysis of loco-rs/loco@23639d1e36 (2026-09-12). Data as JSON: /api/errors/5c84e808aceb0dbd. Report an issue: GitHub.

Appendix: source

Thrown at loco-new/src/generator/template.rs:114

        let mut tera_instance = Tera::default();
        self.register_filters(&mut tera_instance);

        let mut context = Context::new();
        context.insert("settings", &settings);

        let rendered_output = tera_instance.render_str(template_content, &context)?;

        Ok(rendered_output)
    }

    /// Removes the ".t" extension from a template file path, if present.
    ///
    /// # Errors
    /// if the given path is not contains template extension
    pub fn strip_template_extension(&self, path: &Path) -> std::io::Result<PathBuf> {
        path.file_stem().map_or_else(
            || {
                Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidInput,
                    "Failed to retrieve file stem",
                ))
            },
            |stem| {
                let mut path_without_extension = path.to_path_buf();
                path_without_extension.set_file_name(stem);
                if let Some(parent_dir) = path.parent() {
                    path_without_extension = parent_dir.join(stem.to_string_lossy().to_string());
                }
                Ok(path_without_extension)
            },
        )
    }
}

#[cfg(test)]
mod tests {

View on GitHub (pinned to 23639d1e36)