hashicorp/terraform · error

error reading output values

Error message

error reading output values: %s

What it means

After parsing the state file, Put calls jsonstate.MarshalOutputs on stateFile.State.RootOutputValues to derive the JSON output map TFC stores alongside the state. Failure here means the in-memory state's output values can't be converted to the JSON-serializable form TFC expects.

Solutions

  1. Use a Terraform build where core's output types and jsonstate.MarshalOutputs are in sync (rebuild/pin a consistent version).
  2. Reduce outputs to plain primitive types to isolate which output value triggers the marshal failure.
  3. Report the failing output type/marking — this is generally an internal bug, not a config-level fix.
Defensive patterns

Strategy: try-catch

Try / catch

// Capture the marshal error and degrade gracefully
ov, err := jsonstate.MarshalOutputs(s.RootOutputValues)
if err != nil {
    log.Printf("output marshal failed (%v); uploading state without JSON outputs", err)
    ov = nil // proceed with nil outputs rather than aborting
}

Prevention

When it happens

Trigger: jsonstate.MarshalOutputs returns an error: an output value has a type or marked-sensitive shape the marshaler can't handle (e.g. a value type introduced in a newer core than the jsonstate package understands), or internal state struct invariants are violated.

Common situations: Version mismatch where core produced outputs (new cty refinement / nested type) that the bundled jsonstate MarshalOutputs can't encode; experimental features in use; corrupted in-memory state after a partial refresh.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/0a2121704241f7ef. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote/backend_state.go:122

		return fmt.Errorf("error uploading state in compatibility mode: %v", err)
	}
	return err
}

// 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

View on GitHub (pinned to d32a084675)