hashicorp/terraform · critical

FormatValueStr error (dynamic)

Error message

FormatValueStr error (dynamic)

What it means

This panic fires in the diagnostic-expression-value formatter when tfdiags.FormatValueStr(val) returns a non-nil error for a single value inside a failed test-run assertion. FormatValueStr errors when cty.Transform or ctyjson.Marshal fails — i.e. the cty.Value is malformed, has an unsupported type for JSON, or carries marks that break marshalling.

Source

Thrown at internal/command/views/json/diagnostic.go:278

							continue
						}

						traversalStr := tfdiags.TraversalStr(traversal)
						if _, exists := seen[traversalStr]; exists {
							continue Traversals // don't show duplicates when the same variable is referenced multiple times
						}
						value := DiagnosticExpressionValue{
							Traversal: traversalStr,
						}

						// If the diagnostic is caused by a failed run assertion,
						// we'll redact sensitive and ephemeral values within traversals, but format
						// the values in a more human-readable way than the general case.
						// If the value is unknown, we'll leave it to the general case to handle.
						if testDiag != nil && val.IsKnown() {
							valBuf, err := tfdiags.FormatValueStr(val)
							if err != nil {
								panic(err)
							}
							value.Statement = fmt.Sprintf("is %s", valBuf)
							values = append(values, value)
							seen[traversalStr] = struct{}{}
							continue Traversals
						}

						stmt, ok := statementStr(val, includeUnknown, includeSensitive, includeEphemeral)
						if !ok {
							continue Traversals
						}

						value.Statement = stmt
						values = append(values, value)
						seen[traversalStr] = struct{}{}
					}
				}
				sort.Slice(values, func(i, j int) bool {

View on GitHub (pinned to d32a084675)

Solutions

  1. Simplify the assertion expression in the test fixture (prefer primitive or simple collection values) and re-run `terraform test`.
  2. If you maintain tfdiags.FormatValueStr, return an error-string fallback rather than panic-ing in the renderer.
  3. Report the value's cty type/marks from the panic stack trace to narrow down which cty path is unserializable.

Example fix

// before — renderer panics on marshal error
valBuf, err := tfdiags.FormatValueStr(val)
if err != nil { panic(err) }
// after — degrade gracefully (maintainer fix)
valBuf, err := tfdiags.FormatValueStr(val)
if err != nil {
  value.Statement = fmt.Sprintf("is <unable to render value: %s>", err)
} else {
  value.Statement = fmt.Sprintf("is %s", valBuf)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Probe-render the value before relying on it.
if _, err := tfdiags.FormatValueStr(val); err != nil {
  log.Printf("warn: unrenderable value: %v", err)
}

Type guard

// n/a — cty.Value; cannot statically narrow serializability

Try / catch

valBuf, err := tfdiags.FormatValueStr(val)
if err != nil {
  value.Statement = fmt.Sprintf("is <unrenderable: %s>", err)
} else {
  value.Statement = fmt.Sprintf("is %s", valBuf)
}

Prevention

When it happens

Trigger: Evaluating a `terraform test` run-block assertion that failed, while rendering its expression-value diagnostics as JSON. The value must be known (!val.IsKnown() is excluded above). Fires when that known value's cty.Transform or ctyjson.Marshal step returns an error — e.g. a nested value of a type ctyjson cannot serialize, or a value carrying a custom mark unknown to the marshaller.

Common situations: A test fixture references a value of an unusual cty type (e.g. a capsule type, a dynamic pseudo-type, or a deep nested object) inside an assertion, and the diagnostic renderer crashes instead of degrading gracefully. Rare; most primitive/collection/object types marshal cleanly.

Related errors


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