hashicorp/terraform · error
failed to marshal outputs to json
Error message
failed to marshal outputs to json: %w
What it means
Thrown during PersistState when Go's standard encoding/json.Marshal fails to serialize the output value structure produced by jsonstate.MarshalOutputs into the final JSONStateOutputs byte slice. Unlike the preceding translate step (which handles cty-to-JSON type mapping), this is pure JSON encoding of an already-translated Go structure. Failure indicates the translated structure contains a non-serializable Go value.
Solutions
- Upgrade terraform to the latest stable release — json marshaling bugs in outputs are fixed promptly
- Simplify the root outputs to isolate which value produces the unserializable structure
- Report the issue with the json.Marshal error text (it names the Go type that failed) and the terraform/provider versions
Defensive patterns
Strategy: validation
Validate before calling
// Validate that translated outputs are JSON-serializable before persist:
ov, err := jsonstate.MarshalOutputs(state.RootOutputValues)
if err != nil {
return err
}
if _, err := json.Marshal(ov); err != nil {
return fmt.Errorf("outputs are not JSON-serializable, cannot persist: %w", err)
} Try / catch
err := stateMgr.PersistState(schemas)
if err != nil && strings.Contains(err.Error(), "failed to marshal outputs to json") {
log.Print("output JSON marshaling failed; this is likely a terraform bug")
}
return err Prevention
- Report persistent JSON marshaling failures as terraform bugs since the translated structure should always be serializable
- Keep terraform updated to benefit from output-marshaling fixes
- Minimize exotic output types in root module outputs
When it happens
Trigger: jsonstate.MarshalOutputs returns a structure containing a Go type json.Marshal cannot handle (func, chan, complex128, or a type with a broken MarshalJSON); the structure contains an unserializable cty.Value marker that leaked through translation; a bug in MarshalOutputs producing map keys that are not strings.
Common situations: Rare in stable releases; most commonly a terraform bug discovered in a new output type. Can appear when a provider's custom output serialization interacts badly with a terraform version change.
Related errors
- failed to translate outputs
- could not interpret output
- could not marshal output
- error converting output values to json
- error reading output values
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/10d443efce81eec0.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cloud/state.go:228
if schemas != nil {
jsonState, err = jsonstate.Marshal(f, schemas)
if err != nil {
return err
}
}
stateFile, err := statefile.Read(bytes.NewReader(buf.Bytes()))
if err != nil {
return fmt.Errorf("failed to read state: %w", err)
}
ov, err := jsonstate.MarshalOutputs(stateFile.State.RootOutputValues)
if err != nil {
return fmt.Errorf("failed to translate outputs: %w", err)
}
jsonStateOutputs, err := json.Marshal(ov)
if err != nil {
return fmt.Errorf("failed to marshal outputs to json: %w", err)
}
err = s.uploadState(s.lineage, s.serial, s.forcePush, buf.Bytes(), jsonState, jsonStateOutputs)
if err != nil {
s.stateUploadErr = true
return fmt.Errorf("error uploading state: %w", err)
}
// After we've successfully persisted, what we just wrote is our new
// reference state until someone calls RefreshState again.
// We've potentially overwritten (via force) the state, lineage
// and / or serial (and serial was incremented) so we copy over all
// three fields so everything matches the new state and a subsequent
// operation would correctly detect no changes to the lineage, serial or state.
s.readState = s.state.DeepCopy()
s.readLineage = s.lineage
s.readSerial = s.serial
return nilView on GitHub (pinned to d32a084675)