lima-vm/lima · error
failed to marshal as JSON: %+v: %w
Error message
failed to marshal as JSON: %+v: %w
What it means
The "json" template function in textutil.TemplateFuncMap encodes its argument with encoding/json and panics if json.Encoder.Encode fails. Because text/template function panics propagate out of template execution, this error surfaces as a panic (recovered as an execution error by template.Execute) wrapped with the offending value and the underlying marshal error. JSON encoding of in-memory values essentially only fails for unsupported types (channels, funcs, complex numbers) or cyclic data.
Source
Thrown at pkg/textutil/textutil.go:62
return PrefixString(prefix, text)
}
// MissingString returns message if the text is empty.
func MissingString(message, text string) string {
if text == "" {
return message
}
return text
}
// TemplateFuncMap is a text/template FuncMap.
var TemplateFuncMap = template.FuncMap{
"json": func(v any) string {
var b bytes.Buffer
enc := json.NewEncoder(&b)
enc.SetEscapeHTML(false)
if err := enc.Encode(v); err != nil {
panic(fmt.Errorf("failed to marshal as JSON: %+v: %w", v, err))
}
return strings.TrimSuffix(b.String(), "\n")
},
"yaml": func(v any) string {
var b bytes.Buffer
enc := yaml.NewEncoder(&b)
if err := enc.Encode(v); err != nil {
panic(fmt.Errorf("failed to marshal as YAML: %+v: %w", v, err))
}
return "---\n" + strings.TrimSuffix(b.String(), "\n")
},
"indent": func(a ...any) (string, error) {
if len(a) == 0 {
return "", errors.New("function takes at least one string argument")
}
if len(a) > 2 {
return "", errors.New("function takes at most 2 arguments")
}View on GitHub (pinned to dd909d0973)
Solutions
- Inspect the wrapped error message to identify the unsupported field/value, then remove it or change its type before rendering (e.g. use a plain string or []byte instead of a func/chan field).
- Break data cycles: build an acyclic view (copy needed fields into a map/struct without back-pointers) before passing it to the template.
- Implement json.Marshaler on the offending type to emit a serializable representation.
- If you control the call site, prefer executing the template step inside a recover, or marshal the value yourself with json.Marshal and handle the error instead of using the panicking template func.
Example fix
// before (panics: Inst has a chan field)
tmpl.Execute(w, inst) // template: {{json .Inst}}
// after
view := map[string]any{"name": inst.Name, "dir": inst.Dir}
tmpl.Execute(w, view) // {{json .}} works Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := json.Marshal(v); err != nil {
// sanitize v (drop funcs/chans, break cycles) before rendering the template
} Type guard
func jsonSafe(v any) bool {
switch v.(type) {
case chan any, func(), complex64, complex128, map[any]any:
return false
}
return true
} Try / catch
defer func() {
if r := recover(); r != nil {
if strings.Contains(fmt.Sprint(r), "failed to marshal as JSON") {
err = fmt.Errorf("template data not JSON-serializable: %v", r)
}
}
}()
b, err = textutil.ExecuteTemplate(tmpl, data) Prevention
- Only pass JSON-serializable data (no chan/func fields, no cycles) into templates that use {{json}}.
- Pre-marshal risky values with json.Marshal and pass the resulting string into the template.
- Keep template data as plain maps/structs of primitives.
- Always call ExecuteTemplate with error handling; template panics surface as execution errors.
When it happens
Trigger: Executing a text/template (via textutil.ExecuteTemplate or any template using TemplateFuncMap) that calls `{{json v}}` where v contains a value json cannot encode: a channel, function, complex number, an invalid UTF-8 string (skipped, not an error), or most commonly a data cycle causing "json: unsupported value" / "encountered a cycle via ...".
Common situations: Passing a struct with func or chan fields into a template that emits it with {{json .}}; templates rendering Lima config objects that gained a non-serializable field; hand-built maps containing callbacks used inside provisioning templates.
Related errors
- failed to marshal as YAML: %+v: %w
- failed to marshal instance config: %w
- failed to marshal instance %#q: %w
- failed to unmarshal instance response: %w
- failed to unmarshal line %#q: %w
AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01).
Data as JSON: /api/errors/754fa0f4afa64ae7.
Report an issue: GitHub.