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.extension

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Verify the directory exists inside the engine's FS (fs.WalkDir or os.Stat for os-based FS) before calling RootDir.
  2. Prefer passing the root at construction: view.Handlebars(view.FS(fsys), "templates") rather than post-construction RootDir.
  3. 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

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.