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

  1. Treat as an internal bug — capture the wrapped error and the offending output set, then file an issue.
  2. Pin to a known-good Terraform release if it appeared after an upgrade.
  3. 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

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


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)