hashicorp/terraform · error
error converting output values to json
Error message
error converting output values to json: %s
What it means
Standard json.Marshal over the object produced by jsonstate.MarshalOutputs. Under normal operation this cannot fail (cty-derived maps marshal cleanly), so reaching it indicates an unexpected, non-JSON-encodable value slipped into the output map.
Solutions
- Treat as an internal bug — capture the wrapped error and the offending output set, then file an issue.
- Pin to a known-good Terraform release if it appeared after an upgrade.
- Temporarily remove the most recently added output to confirm which value triggers it.
Defensive patterns
Strategy: try-catch
Try / catch
// json.Marshal over a cty-derived map should not fail; treat as fatal-but-contained
o, err := json.Marshal(ov)
if err != nil {
return fmt.Errorf("internal: outputs unencodable (%w); see issue tracker", err)
} Prevention
- Treat as an internal invariant violation — capture and file a bug.
- Pin Terraform core to a release where outputs marshal reliably.
- Reduce output surface to isolate which value is unencodable.
When it happens
Trigger: json.Marshal returns an error: a channel/func/unsupported type ended up in the marshaled map, a recursive structure, or an encoding error from a malformed cty value conversion. Practically unreachable.
Common situations: Forked jsonstate returning non-canonical types; bug in output-value sensitive-marking handling producing an unencodable wrapper; memory corruption.
Related errors
- error reading output values
- could not interpret output
- could not marshal output
- Error checking remote Terraform version
- Error downloading state
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/d9ef45640d70f267.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote/backend_state.go:126
// Put the remote state.
func (r *remoteClient) Put(state []byte) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
ctx := context.Background()
// Read the raw state into a Terraform state.
stateFile, err := statefile.Read(bytes.NewReader(state))
if err != nil {
return diags.Append(fmt.Errorf("error reading state: %s", err))
}
ov, err := jsonstate.MarshalOutputs(stateFile.State.RootOutputValues)
if err != nil {
return diags.Append(fmt.Errorf("error reading output values: %s", err))
}
o, err := json.Marshal(ov)
if err != nil {
return diags.Append(fmt.Errorf("error converting output values to json: %s", err))
}
options := tfe.StateVersionUploadOptions{
StateVersionCreateOptions: tfe.StateVersionCreateOptions{
Lineage: tfe.String(stateFile.Lineage),
Serial: tfe.Int64(int64(stateFile.Serial)),
MD5: tfe.String(fmt.Sprintf("%x", md5.Sum(state))),
Force: tfe.Bool(r.forcePush),
JSONStateOutputs: tfe.String(base64.StdEncoding.EncodeToString(o)),
},
RawState: state,
}
// If we have a run ID, make sure to add it to the options
// so the state will be properly associated with the run.
if r.runID != "" {
options.Run = &tfe.Run{ID: r.runID}
}View on GitHub (pinned to d32a084675)