hashicorp/terraform · critical

json.MarshalIndent error (dynamic)

Error message

json.MarshalIndent error (dynamic)

What it means

Terraform panics in ShowJSON.DisplayResourceInstanceState when json.MarshalIndent fails on the 'terraform state show -json <addr>' output struct (FormatVersion + a jsonstate.Resource + diagnostics). The code comment asserts this 'should never happen because we fully-control the input', so a panic here means the resource state being serialized contains a value encoding/json cannot represent.

Source

Thrown at internal/command/views/show.go:231

		output.Diagnostics = []*viewsjson.Diagnostic{}
	}

	var rs jsonstate.Resource
	if !jsonState.Empty() {
		// we know there's only one resource instance, but we need to find it.
		if len(jsonState.RootModule.Resources) > 0 {
			rs = jsonState.RootModule.Resources[0]
		} else {
			rs = findResourceInChildModules(jsonState.RootModule)
		}
	}

	output.Resource = rs

	j, err := json.MarshalIndent(&output, "", "  ")
	if err != nil {
		// Should never happen because we fully-control the input here
		panic(err)
	}
	v.view.streams.Println(string(j))

	if diags.HasErrors() {
		return 1
	}
	return 0
}

func findResourceInChildModules(mod jsonstate.Module) jsonstate.Resource {
	for _, cm := range mod.ChildModules {
		if len(cm.Resources) == 1 {
			return cm.Resources[0]
		}
	}
	for _, child := range mod.ChildModules {
		return findResourceInChildModules(child)
	}

View on GitHub (pinned to d32a084675)

Solutions

  1. Inspect the exact resource address and provider involved; reproduce with 'terraform state show -json <addr>' and capture the stack trace, then check the resource attributes for NaN/Inf or non-serializable values.
  2. If the state is corrupted, use 'terraform state pull' / edit the JSON / 'terraform state push' to remove the offending attribute, or restore a known-good state backup from the workspace .terraform/ or remote backend history.
  3. Report the bug to Terraform with the goroutine trace and the provider/schema that produced the unmarshallable value; the defensive panic indicates a serialization contract was violated upstream.
  4. Pin to a known-good provider/binary version that does not emit the offending value while the upstream fix lands.

Example fix

// before
j, err := json.MarshalIndent(&output, "", "  ")
if err != nil {
    // Should never happen because we fully-control the input here
    panic(err)
}

// after (fail gracefully instead of crashing the CLI)
j, err := json.MarshalIndent(&output, "", "  ")
if err != nil {
    v.view.streams.Eprintf("Failed to marshal resource state to json: %s", err)
    return 1
}
Defensive patterns

Strategy: validation

Validate before calling

// Before calling DisplayResourceInstanceState, sanity-check the resource you
// are about to render does not contain non-serializable numeric values.
func isMarshallableResource(rs jsonstate.Resource) error {
    // jsonstate.Resource marshals via encoding/json; the only realistic
    // failure modes are NaN/Inf in numeric attributes or a custom
    // MarshalJSON error. Walk the attribute JSON if you have it.
    raw, err := json.Marshal(rs)
    if err != nil {
        return fmt.Errorf("resource would fail json marshal: %w", err)
    }
    _ = raw
    return nil
}

Try / catch

// Wrap CLI entry points so an unexpected MarshalIndent panic degrades to a
// non-zero exit + message instead of an uncaught goroutine crash.
func runShowJSON(ctx context.Context, fn func() int) (code int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Fprintf(os.Stderr, "terraform: aborted show -json: %v\n", r)
            code = 1
        }
    }()
    return fn()
}

Prevention

When it happens

Trigger: Calling view.DisplayResourceInstanceState in ShowJSON (internal/command/views/show.go:228) where the assembled Output struct holds a jsonstate.Resource whose MarshalJSON or nested cty/JSON value returns an error. json.MarshalIndent returns a non-nil error for unsupported values: chan/func/math.NaN/math.Inf/cyclic refs, or a custom MarshalJSON that errors.

Common situations: A state file built from a provider whose schema produced a numeric value that became NaN/Inf, hand-edited or corrupted state, a third-party provider emitting a value Terraform's jsonstate marshaller cannot round-trip, or an internal regression in jsonstate.Resource marshaling after a schema change.

Related errors


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