{"record":{"id":"43a65785bc895ca1","repo":"rwf2/Rocket","slug":"invalidoperation","errorCode":"InvalidOperation","errorMessage":"template read failed","messagePattern":"template read failed","errorType":"exception","errorClass":"minijinja::Error","httpStatus":null,"severity":"error","filePath":"contrib/dyn_templates/src/engine/minijinja.rs","lineNumber":29,"sourceCode":"    const EXT: &'static str = \"j2\";\n\n    fn init<'a>(templates: impl Iterator<Item = (&'a str, &'a Path)>) -> Option<Self> {\n        let _templates = Arc::new(templates\n            .map(|(k, p)| (k.to_owned(), p.to_owned()))\n            .collect::<HashMap<_, _>>());\n\n        let templates = _templates.clone();\n        let mut env = Environment::new();\n        env.set_loader(move |name| {\n            let Some(path) = templates.get(name) else {\n                return Ok(None);\n            };\n\n            match std::fs::read_to_string(path) {\n                Ok(result) => Ok(Some(result)),\n                Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),\n                Err(e) => Err(\n                    Error::new(ErrorKind::InvalidOperation, \"template read failed\").with_source(e)\n                ),\n            }\n        });\n\n        let templates = _templates.clone();\n        env.set_auto_escape_callback(move |name| {\n            templates.get(name)\n                .and_then(|path| path.to_str())\n                .map(minijinja::default_auto_escape_callback)\n                .unwrap_or(AutoEscape::None)\n        });\n\n        Some(env)\n    }\n\n    fn render<C: Serialize>(&self, template: &str, context: C) -> Option<String> {\n        let Ok(templ) = self.get_template(template) else {\n            error!(template, \"requested template does not exist\");","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/rwf2/Rocket/blob/3a54d079aef060a8f732bd04ea54b0581a604087/contrib/dyn_templates/src/engine/minijinja.rs#L11-L47","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"# before (file unreadable by server user)\n$ chmod 600 templates/index.html   # server runs as www-data\n\n# after\n$ chmod 644 templates/index.html\n$ find templates/ -type d -exec chmod a+rx {} \\;","handlingStrategy":"try-catch","validationCode":"// before rendering, confirm every needed template is readable\nuse std::fs;\nfn templates_readable(dir: &std::path::Path, names: &[&str]) -> std::io::Result<()> {\n    for n in names {\n        let p = dir.join(format!(\"{n}.hbs\")); // or .html for minijinja\n        let meta = fs::metadata(&p)?;\n        if meta.is_dir() { return Err(std::io::Error::new(std::io::ErrorKind::IsADirectory, p.display().to_string())); }\n        fs::File::open(&p)?; // proves readability under current user\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"let rendered = match tmpl.render(\"index\", &ctx) {\n    Ok(body) => body,\n    Err(e) if e.kind() == minijinja::ErrorKind::InvalidOperation => {\n        error_!(\"template unreadable: {e}\");\n        return Status::InternalServerError;\n    },\n    Err(e) => {\n        error_!(\"render failed: {e}\");\n        return Status::InternalServerError;\n    },\n};","preventionTips":["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"],"tags":["rust","rocket","minijinja","templates","filesystem","permissions"],"backgroundTag":"file-permission-denied","analyzedSha":"3a54d079aef060a8f732bd04ea54b0581a604087","analyzedAt":"2026-08-16T22:01:48.395Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}