rwf2/Rocket · error · minijinja::Error
InvalidOperation
InvalidOperation
Error message
template read failed
What it means
This error is produced by rocket_dyn_templates' MiniJinja engine when a template name resolves to a registered path on disk, but reading that file fails with an I/O error other than NotFound. Note that a missing template is deliberately treated as Ok(None) (minijinja then reports 'template not found'), so this error means the path exists but cannot be read, e.g. a permission error or the path points at a directory. It is wrapped in minijinja's ErrorKind::InvalidOperation with the underlying io::Error as its source, and surfaces when a template is loaded/rendered.
Source
Thrown at contrib/dyn_templates/src/engine/minijinja.rs:29
const EXT: &'static str = "j2";
fn init<'a>(templates: impl Iterator<Item = (&'a str, &'a Path)>) -> Option<Self> {
let _templates = Arc::new(templates
.map(|(k, p)| (k.to_owned(), p.to_owned()))
.collect::<HashMap<_, _>>());
let templates = _templates.clone();
let mut env = Environment::new();
env.set_loader(move |name| {
let Some(path) = templates.get(name) else {
return Ok(None);
};
match std::fs::read_to_string(path) {
Ok(result) => Ok(Some(result)),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(e) => Err(
Error::new(ErrorKind::InvalidOperation, "template read failed").with_source(e)
),
}
});
let templates = _templates.clone();
env.set_auto_escape_callback(move |name| {
templates.get(name)
.and_then(|path| path.to_str())
.map(minijinja::default_auto_escape_callback)
.unwrap_or(AutoEscape::None)
});
Some(env)
}
fn render<C: Serialize>(&self, template: &str, context: C) -> Option<String> {
let Ok(templ) = self.get_template(template) else {
error!(template, "requested template does not exist");View on GitHub (pinned to 3a54d079ae)
Solutions
- Check read permissions on the file and every parent directory for the server user: sudo -u www-data cat templates/index.html
- Verify the path is a regular file, not a directory: ls -ld $(find temp_dir -name '<template>')
- Confirm temp_dir/templates dir config in Rocket.toml points at the directory you expect (relative to the CWD the binary is launched from)
- If running in a container, ensure the template directory is actually copied/mounted and not masked by a volume
Example fix
# before (file unreadable by server user)
$ chmod 600 templates/index.html # server runs as www-data
# after
$ chmod 644 templates/index.html
$ find templates/ -type d -exec chmod a+rx {} \; Defensive patterns
Strategy: try-catch
Validate before calling
// before rendering, confirm every needed template is readable
use std::fs;
fn templates_readable(dir: &std::path::Path, names: &[&str]) -> std::io::Result<()> {
for n in names {
let p = dir.join(format!("{n}.hbs")); // or .html for minijinja
let meta = fs::metadata(&p)?;
if meta.is_dir() { return Err(std::io::Error::new(std::io::ErrorKind::IsADirectory, p.display().to_string())); }
fs::File::open(&p)?; // proves readability under current user
}
Ok(())
} Try / catch
let rendered = match tmpl.render("index", &ctx) {
Ok(body) => body,
Err(e) if e.kind() == minijinja::ErrorKind::InvalidOperation => {
error_!("template unreadable: {e}");
return Status::InternalServerError;
},
Err(e) => {
error_!("render failed: {e}");
return Status::InternalServerError;
},
}; Prevention
- Add a startup check (fairing on_ignite) that reads every template once so failures happen at boot, not mid-request
- Run the service under the same user that owns the templates in every environment
- Use absolute or config-driven template dir paths rather than CWD-relative globs
When it happens
Trigger: Calling Template::render / render_template on a route after rocket_dyn_templates is attached, where the template glob matched a file that later becomes unreadable: chmod 000 or chown to another user on a .html file, a template path that is actually a directory, or an unreadable path inside a symlinked dir. Also occurs if the process' runtime user (systemd service, container) lacks read permission on the template directory.
Common situations: Deploying behind a service user with different file ownership than the build/deploy user; Docker images that COPY templates with restrictive modes; NFS-mounted template dirs; a stale glob (templ_dir changed in Rocket.toml but old files remain as directories or broken symlinks).
Related errors
AI-assisted analysis of rwf2/Rocket@3a54d079ae (2026-08-16).
Data as JSON: /api/errors/43a65785bc895ca1.
Report an issue: GitHub.