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
- Wrap non-map data in a map: ctx.View("page.html", map[string]any{"items": data}).
- Pass structs by value (or as pointer auto-deref supported by structs.IsStruct) so the struct->map branch runs.
- If data can be nil, guard before View: pass map[string]any{} instead.
- 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
- Always wrap slices/scalars: map[string]any{"key": val}.
- Never pass nil directly to ctx.View; pass an empty map.
- Add a lint/test that exercises every template render path.
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.