twigphp/Twig · error · Twig\Error\RuntimeError
The " " attribute value cannot be JSON encoded.
Error message
The "%s" attribute value cannot be JSON encoded.
What it means
html_attr_value() JSON-encodes non-scalar, non-Stringable, non-null attribute values so they can be rendered. If json_encode() with JSON_THROW_ON_ERROR fails (e.g. malformed UTF-8, recursion, unsupported resources), the JsonException is wrapped in this RuntimeError with the attribute name for context.
Solutions
- Fix the underlying data: sanitize invalid UTF-8 (e.g. mb_convert_encoding($s, 'UTF-8', 'UTF-8')).
- Implement Stringable on the value object so it is used verbatim instead of JSON-encoded.
- Convert the value to a scalar/array of scalars before passing.
- Inspect $e->getPrevious() (the JsonException) to see the exact encoding failure.
Example fix
// before html_attr(['data-payload' => $rawBinary]) // after html_attr(['data-payload' => base64_encode($rawBinary)])
Defensive patterns
Strategy: try-catch
Validate before calling
foreach ($values as $v) { if (!is_scalar($v) && !$v instanceof \Stringable && null !== $v) json_encode($v, JSON_THROW_ON_ERROR); } Type guard
function isJsonEncodableAttr(mixed $v): bool { try { if (is_scalar($v) || $v instanceof \Stringable || null === $v) return true; json_encode($v, JSON_THROW_ON_ERROR); return true; } catch (\JsonException) { return false; } } Try / catch
try { $html = html_attr($attrs); } catch (Twig\Error\RuntimeError $e) { if ($e->getPrevious() instanceof \JsonException) { /* sanitize and retry */ } throw $e; } Prevention
- Sanitize strings to valid UTF-8 before using as attributes
- Prefer scalars or Stringable objects for attribute values
- Base64-encode binary payloads
When it happens
Trigger: Passing an array/object containing resources, circular references or invalid UTF-8 strings as an attribute value in html_attr().
Common situations: Binary data or malformed UTF-8 from legacy databases placed into attributes; recursively-referencing objects; DOMDocument or PDO objects accidentally passed as values.
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
- Unknown attribute type
- Only empty strings may be passed as string arguments to…
- Cannot merge incompatible values for key
- The " " attribute value should be a scalar, an iterable, or…
- Unknown " " configuration.
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/d043ef6d40ab4fe8.
Report an issue: GitHub.
Appendix: source
Thrown at extra/html-extra/HtmlExtension.php:247
}
if (str_starts_with($name, 'aria-')) {
// For aria-*, convert booleans to "true" and "false" strings
if (true === $value) {
$value = 'true';
} elseif (false === $value) {
$value = 'false';
}
}
if (str_starts_with($name, 'data-')) {
if (!$value instanceof AttributeValueInterface && !$value instanceof \Stringable && null !== $value && !\is_scalar($value)) {
// ... encode non-null non-scalars as JSON, but leave the string representation
// of a Stringable alone, as it is already the value the object asks to render as
try {
$value = json_encode($value, \JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
throw new RuntimeError(\sprintf('The "%s" attribute value cannot be JSON encoded.', $name), previous: $e);
}
} elseif (true === $value) {
// ... and convert boolean true to a 'true' string.
$value = 'true';
}
}
// Convert iterable values to token lists
if (!$value instanceof AttributeValueInterface && is_iterable($value)) {
if ('style' === $name) {
$value = new InlineStyle($value);
} else {
$value = new SeparatedTokenList($value);
}
}
if ($value instanceof AttributeValueInterface) {
$value = $value->getValue();View on GitHub (pinned to a414c3a491)