kataras/iris · error

please provide a map[string]any type as the binding instead

Error message

please provide a map[string]any type as the binding instead of the %#v

What it means

The Handlebars engine requires the view binding data to be a map[string]any when rendering with a layout, because it injects the rendered partial into the layout context via a "yield" key on that map. If ExecuteWriter is called with a layout and any other binding type (struct, pointer, slice, nil non-map), the engine cannot attach the yield value and returns this error instead of rendering.

Source

Thrown at view/handlebars.go:238

	}

	isLayout := false
	layout = getLayout(layout, s.layout)
	renderFilename := filename

	if layout != "" {
		isLayout = true
		renderFilename = layout // the render becomes the layout, and the name is the partial.
	}

	if tmpl := s.fromCache(renderFilename); tmpl != nil {
		binding := bindingData
		if isLayout {
			var context map[string]any
			if m, is := binding.(map[string]any); is { // handlebars accepts maps,
				context = m
			} else {
				return fmt.Errorf("please provide a map[string]any type as the binding instead of the %#v", binding)
			}

			contents, err := s.executeTemplateBuf(filename, binding)
			if err != nil {
				return err
			}
			if context == nil {
				context = make(map[string]any, 1)
			}
			// I'm implemented the {{ yield . }} as with the rest of template engines, so this is not inneed for iris, but the user can do that manually if want
			// there is no performance cost: raymond.RegisterPartialTemplate(name, tmpl)
			context["yield"] = raymond.SafeString(contents)
		}

		res, err := tmpl.Exec(binding)
		if err != nil {
			return err
		}

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Convert the binding to map[string]any before calling ExecuteWriter when a layout is used.
  2. Render without a layout (pass view.NoLayout as the layout argument) if a struct binding is required.
  3. Marshal the struct to a map via a helper (e.g. json round-trip or a ToMap function) so any struct can still be used.

Example fix

// before
err := view.ExecuteWriter(w, "index", "layouts/main", myStruct)
// after
binding := map[string]any{"Title": myStruct.Title, "Items": myStruct.Items}
err := view.ExecuteWriter(w, "index", "layouts/main", binding)
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := binding.(map[string]any); !ok && layout != "" {
    return fmt.Errorf("handlebars layout rendering requires map[string]any binding, got %T", binding)
}

Type guard

func isMapBinding(v any) (map[string]any, bool) {
    m, ok := v.(map[string]any)
    return m, ok
}

Try / catch

if err := v.ExecuteWriter(w, "index.html", "layouts/main.html", binding); err != nil {
    var bindErr *fmt.wrapError // inspect for type-mismatch message
    log.Printf("render failed: %v", err)
    http.Error(w, "template error", http.StatusInternalServerError)
}

Prevention

When it happens

Trigger: Calling HTMLEngine/Handlebars ExecuteWriter(w, filename, layout, binding) where layout is non-empty (or a global layout is configured) and binding is not a map[string]any — e.g. a struct, *struct, map[string]string, or any custom type.

Common situations: Developers render with a layout while passing a typed view-model struct that works fine without layouts; or after migrating from another engine (html/template accepts structs) to handlebars; or passing map[string]string which is not map[string]any.

Related errors


AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30). Data as JSON: /api/errors/d9234c31db29c237. Report an issue: GitHub.