kataras/iris · error
err
Error message
err
What it means
HandlebarsEngine.RootDir re-roots the engine's filesystem with fs.Sub(s.fs, s.rootDir) when the engine FS is already initialized and a different root is requested. An fs.Sub error (directory not present in the FS) causes a panic because templates could never be resolved under the missing root.
Source
Thrown at view/handlebars.go:74
// register the render helper here
raymond.RegisterHelper("render", func(partial string, binding any) raymond.SafeString {
contents, err := s.executeTemplateBuf(partial, binding)
if err != nil {
return raymond.SafeString("template with name: " + partial + " couldn't not be found.")
}
return raymond.SafeString(contents)
})
return s
}
// RootDir sets the directory to be used as a starting point
// to load templates from the provided file system.
func (s *HandlebarsEngine) RootDir(root string) *HandlebarsEngine {
if s.fs != nil && root != "" && root != "/" && root != "." && root != s.rootDir {
sub, err := fs.Sub(s.fs, s.rootDir)
if err != nil {
panic(err)
}
s.fs = sub // here so the "middleware" can work.
}
s.rootDir = filepath.ToSlash(root)
return s
}
// Name returns the handlebars engine's name.
func (s *HandlebarsEngine) Name() string {
return "Handlebars"
}
// Ext returns the file extension which this view engine is responsible to render.
// If the filename extension on ExecuteWriter is empty then this is appended.
func (s *HandlebarsEngine) Ext() string {
return s.extensionView on GitHub (pinned to 7bedaf55a0)
Solutions
- Verify the directory exists inside the engine's FS (fs.WalkDir or os.Stat for os-based FS) before calling RootDir.
- Prefer passing the root at construction: view.Handlebars(view.FS(fsys), "templates") rather than post-construction RootDir.
- Keep repeated RootDir calls consistent (same value short-circuits the failing fs.Sub branch).
Example fix
// before
eng := view.Handlebars(view.FS(assets))
eng.RootDir("templates") // panics: no "templates" dir inside assets
// after
eng := view.Handlebars(view.FS(assets), "tpl") // matches actual embedded dir Defensive patterns
Strategy: validation
Validate before calling
if _, err := fs.Stat(engineFS, "templates"); err != nil {
log.Fatalf("handlebars root missing: %v", err)
}
engine.RootDir("templates") Try / catch
defer func() {
if r := recover(); r != nil {
log.Fatalf("Handlebars RootDir failed: %v", r)
}
}() Prevention
- Set the root once at construction with view.Handlebars(view.FS(fsys), root).
- Keep embedded dir names and RootDir strings in sync via a constant.
- Verify template paths in a startup test.
When it happens
Trigger: Calling h.RootDir("partials") on an already-created Handlebars engine whose underlying fs does not contain "partials" at its current root, or chaining RootDir with an inconsistent second path.
Common situations: Mismatches between //go:embed directory names and the string passed to RootDir, moving templates between repo layouts after upgrading, or double-calling RootDir with different values.
Related errors
AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30).
Data as JSON: /api/errors/8e676e4325d30a31.
Report an issue: GitHub.