kataras/iris · error

django: template data: should be a map or struct

Error message

django: template data: should be a map or struct

What it means

The django view engine's context converter accepts only map-like values (and structs, via reflection) as template data. When ExecuteWriter receives data of another kind (nil of unexpected type, slice, scalar) it panics with this message because pongo2 has no context representation for it.

Source

Thrown at view/django.go:302

// getPongoContext returns the pongo2.Context from map[string]any or from pongo2.Context, used internaly
func getPongoContext(templateData any) pongo2.Context {
	if templateData == nil {
		return nil
	}

	switch data := templateData.(type) {
	case pongo2.Context:
		return data
	case context.Map:
		return pongo2.Context(data)
	default:
		// if struct, convert it to map[string]any
		if structs.IsStruct(data) {
			return pongo2.Context(structs.Map(data))
		}

		panic("django: template data: should be a map or struct")
	}
}

func (s *DjangoEngine) fromCache(relativeName string) *pongo2.Template {
	if s.reload {
		s.rmu.RLock()
		defer s.rmu.RUnlock()
	}

	if tmpl, ok := s.templateCache[relativeName]; ok {
		return tmpl
	}
	return nil
}

// ExecuteWriter executes a templates and write its results to the w writer
// layout here is useless.
func (s *DjangoEngine) ExecuteWriter(w io.Writer, filename string, _ string, bindingData any) error {

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Wrap non-map data in a map: ctx.View("page.html", map[string]any{"items": data}).
  2. Pass structs by value (or as pointer auto-deref supported by structs.IsStruct) so the struct->map branch runs.
  3. If data can be nil, guard before View: pass map[string]any{} instead.
  4. Adjust the template to index into the map key rather than expecting the bare value.

Example fix

// before
return ctx.View("list.html", users) // users is []User -> panic
// after
return ctx.View("list.html", map[string]any{"users": users})
Defensive patterns

Strategy: type-guard

Validate before calling

func viewData(v any) any {
	if v == nil { return map[string]any{} }
	switch v.(type) {
	case map[string]any, map[string]string:
		return v
	default:
		if reflect.TypeOf(v).Kind() != reflect.Struct {
			panic("view data must be map or struct")
		}
		return v
	}
}

Type guard

func isMapView(v any) bool {
	if v == nil { return false }
	t := reflect.TypeOf(v)
	if t.Kind() == reflect.Ptr { t = t.Elem() }
	return t.Kind() == reflect.Map || t.Kind() == reflect.Struct
}

Try / catch

defer func() {
	if r := recover(); strings.Contains(fmt.Sprint(r), "template data") {
		log.Printf("bad view data: %v", r)
	}
}()

Prevention

When it happens

Trigger: ctx.View("page.html", someSlice) or ctx.View("page.html", 42) or ctx.View("page.html", nil-ish non-map non-struct value) — getPongoContext falls through the switch to the default branch and panics.

Common situations: Passing a []User list directly instead of wrapping it (map[string]any{"users": users}), passing a pointer to a struct wrapped incorrectly, or returning ORM query results that are slices.

Related errors


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