hashicorp/terraform · error
failed to translate outputs
Error message
failed to translate outputs: %w
What it means
Thrown during PersistState when jsonstate.MarshalOutputs fails to translate the state's RootOutputValues into a JSON-serializable structure. This conversion step walks each output value's cty type and builds a JSON representations; it fails when an output value uses a type or nesting depth the marshaler cannot handle. The %w preserves the marshaler's underlying error.
Solutions
- Identify which output value triggers the failure by checking the wrapped error message for type information
- Temporarily remove or simplify the problematic output (e.g. flatten a deeply nested object) and re-run apply
- Upgrade terraform to the latest patch release to pick up fixes to jsonstate output marshaling
- If the output type originates from a provider, upgrade or pin the provider to a version whose output schema is compatible with your terraform version
Example fix
// before
output "raw_config" {
value = some_resource.this // full resource object, complex type
}
// after
output "raw_config" {
value = some_resource.this.id // simple string
} Defensive patterns
Strategy: validation
Validate before calling
// Before persisting, verify all root outputs can be marshaled:
for name, ov := range state.RootOutputValues {
if ov == nil || ov.Value.IsNull() {
continue
}
// attempt a cty walk to detect unsupported types early
if !ov.Value.Type().IsObjectType() && !ov.Value.Type().IsPrimitiveType() && !ov.Value.Type().IsListType() && !ov.Value.Type().IsMapType() {
log.Printf("[WARN] output %s has potentially unserializable type %s", name, ov.Value.Type().FriendlyName())
}
} Try / catch
err := stateMgr.PersistState(schemas)
if err != nil && strings.Contains(err.Error(), "failed to translate outputs") {
// identify and simplify the offending output, then retry
log.Print("output translation failed; simplify root outputs and retry")
}
return err Prevention
- Keep root outputs simple (primitives, flat lists/maps) to avoid type-marshaling edge cases
- Avoid exposing entire resource objects as outputs; extract specific attributes
- Test output types after provider upgrades before applying to production cloud workspaces
When it happens
Trigger: A root output value has a cty type (e.g. deeply nested object, dynamic/pseudo-type, or null with missing type info) that jsonstate.MarshalOutputs cannot encode; state corrupted by a provider returning an unencodable output type; a terraform version where output type tracking is incomplete.
Common situations: After upgrading a provider that changed an output's type to a complex nested shape; when outputs reference resources whose schema changed between terraform versions; large configurations with many interconnected outputs after a schema-breaking provider update.
Related errors
- failed to marshal outputs to json
- could not decode output
- could not interpret output
- could not interpret value
- could not marshal output
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/40e37df62982bcba.
Report an issue: GitHub.
Appendix: 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 d32a084675)