hashicorp/terraform · error
failed to marshal outputs to json: %w
Error message
failed to marshal outputs to json: %w
What it means
Thrown during PersistState after Terraform marshals root output values: json.Marshal(ov) fails on the map returned by jsonstate.MarshalOutputs. It means an internal serialization of the state's root outputs could not be JSON-encoded for the json-state-outputs field of the state-version upload. This is an internal/serialization failure rather than a normal configuration problem.
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 c9def3e214)
Solutions
- Re-run terraform refresh / terraform apply; if the state is internally valid the re-marshal usually succeeds.
- Inspect root output declarations for exotic or unsupported value shapes and simplify them.
- If reproducible, dump the offending output values and file a Terraform issue with the state version.
- As a workaround, remove or simplify the suspect output block and re-apply to rewrite state.
Example fix
// before: an output holding a non-encodable/internal value
output "x" { value = local.something_odd }
// after: keep outputs to plain, encodable types
output "x" { value = var.simple_string } Defensive patterns
Strategy: try-catch
Try / catch
if err := state.PersistState(schemas); err != nil {
// PersistState already wraps the cause with %w
var jsonErr *json.UnsupportedTypeError
if errors.As(err, &jsonErr) {
// root output contains a non-encodable value type; simplify outputs and retry
return fmt.Errorf("unencodable output value, simplify outputs: %w", err)
}
return err
} Prevention
- Keep root outputs to plain, JSON-encodable types
- Avoid custom/internal values in outputs
- Re-run on transient serialization hiccups before debugging
When it happens
Trigger: PersistState(schemas) is called and one of the root output values produced by jsonstate.MarshalOutputs is not JSON-encodable. In practice this requires a malformed or internally-inconsistent output value object, since well-formed outputs always serialize.
Common situations: Almost always an internal Terraform bug or a corrupted state object, e.g. after a bad state upgrade or a go-cty value that does not round-trip. Normal configs do not trigger it.
Related errors
- error converting output values to json: %s
- failed to translate outputs: %w
- could not marshal output %s type: %w
- error reading output values: %s
- failed to read state: %w
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/10d443efce81eec0.
Report an issue: GitHub.