hashicorp/terraform · error

failed to translate outputs: %w

Error message

failed to translate outputs: %w

What it means

Returned by the state persister when jsonstate.MarshalOutputs(stateFile.State.RootOutputValues) fails while building the JSON representation of state outputs. The %w wraps the marshaling error (e.g. an output value of a type the JSON encoder cannot represent).

Source

Thrown at internal/cloud/state.go:224

		return err
	}

	var jsonState []byte
	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()

View on GitHub (pinned to c9def3e214)

Solutions

  1. Inspect the wrapped error to see which output value failed to marshal.
  2. Simplify or correct the offending output declaration (ensure JSON-serializable types).
  3. Upgrade the provider/Terraform to resolve known output-type marshaling bugs.
  4. As a last resort, run `terraform refresh`/`terraform state push` after correcting outputs.

Example fix

// before
output "roles" { value = nonsensitive({ (rand) = ... }) } // non-string keys -> marshal error
// after: use string keys and JSON-friendly structure
output "roles" { value = { for k, v in local.roles : tostring(k) => v } }
Defensive patterns

Strategy: validation

Validate before calling

// Validate outputs are JSON-serializable before persisting.
for name, ov := range state.RootOutputValues {
    if !jsonSerializable(ov.Value) {
        return fmt.Errorf("output %q is not JSON-serializable", name)
    }
}

Type guard

func jsonSerializable(v cty.Value) bool {
    _, err := json.Marshal(ctyjson.SimpleJSONValue{Value: v})
    return err == nil
}

Try / catch

ov, err := jsonstate.MarshalOutputs(stateFile.State.RootOutputValues)
if err != nil {
    // inspect which output failed; correct the config and re-persist
    return fmt.Errorf("failed to translate outputs: %w", err)
}

Prevention

When it happens

Trigger: After re-reading the serialized state, translating its root output values into the JSON state format fails — typically an output whose cty type cannot be marshaled to JSON (e.g. contains non-string map keys, unsupported nested types), or a schema/type mismatch.

Common situations: Outputs producing unusual cty values (sets/maps of objects, dynamic types), provider output bugs surfacing unusual types, or a state produced by a mismatched Terraform/provider version being re-persisted.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/40e37df62982bcba. Report an issue: GitHub.