twigphp/Twig · error · Twig\Error\RuntimeError
The "html_classes" function argument
Error message
The "html_classes" function argument %d (key %d) should be a string, got "%s".
What it means
The twig html_classes() function builds a class attribute from strings plus array arguments mapping class names to boolean conditions. When an array argument's key is not a string, htmlClasses() throws this RuntimeError with the argument index, the offending key, and its debug type, because non-string keys cannot become CSS class names.
Solutions
- Ensure every array argument uses string class names as keys.
- Cast or rebuild the array with class-name keys before passing.
- Filter out numeric keys before calling html_classes.
- Use string arguments (unconditional classes) instead of arrays when no condition is needed.
Example fix
// before html_classes([0 => true, 1 => false]); // after html_classes(['is-active' => true, 'is-hidden' => false]);
Defensive patterns
Strategy: validation
Validate before calling
foreach ($args as $arg) {
if (is_array($arg)) {
foreach (array_keys($arg) as $key) {
if (!is_string($key)) {
throw new InvalidArgumentException(sprintf('html_classes array keys must be class-name strings, got %s', get_debug_type($key)));
}
}
}
}
html_classes(...$args); Type guard
function hasStringKeys(array $map): bool {
return [] === array_filter(array_keys($map), fn($k) => !is_string($k));
} Try / catch
try {
$classes = html_classes(...$args);
} catch (\Twig\Error\RuntimeError $e) {
if (str_contains($e->getMessage(), 'should be a string')) {
// rebuild args with string keys before retrying
} else {
throw $e;
}
} Prevention
- Only pass class-name-keyed condition maps to html_classes
- Beware PHP coercing numeric-string keys ('5') to int keys in array literals
- Rebuild condition maps with array_combine against a list of class names
- Use plain string arguments for unconditional classes
When it happens
Trigger: Calling html_classes(['0' => true]) with integer keys, html_classes($conditions) where $conditions has int keys, or keys produced by array_values/array_column with numeric indexes.
Common situations: Passing a condition map built from a database result or numeric-indexed array instead of class-name-keyed array; PHP silently converts numeric-string keys like '5' to int keys.
Understand the failure class
Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.
Related errors
- InlineStyle can only be created from iterable values.
- Attributes using InlineStyle can only be merged with…
- Only iterable values can be appended to InlineStyle.
- SeparatedTokenList can only be constructed from iterable or…
- SeparatedTokenList can only be merged with iterables or…
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/638069b8843022b8.
Report an issue: GitHub.
Appendix: source
Thrown at extra/html-extra/HtmlExtension.php:110
$repr .= ';base64,'.base64_encode($data);
}
return $repr;
}
/**
* @internal
*/
public static function htmlClasses(...$args): string
{
$classes = [];
foreach ($args as $i => $arg) {
if (\is_string($arg) || $arg instanceof Markup) {
$classes[] = (string) $arg;
} elseif (\is_array($arg)) {
foreach ($arg as $class => $condition) {
if (!\is_string($class)) {
throw new RuntimeError(\sprintf('The "html_classes" function argument %d (key %d) should be a string, got "%s".', $i, $class, get_debug_type($class)));
}
if (!$condition) {
continue;
}
$classes[] = $class;
}
} else {
throw new RuntimeError(\sprintf('The "html_classes" function argument %d should be either a string or an array, got "%s".', $i, get_debug_type($arg)));
}
}
return implode(' ', array_unique(array_filter($classes, static function ($v) { return '' !== $v; })));
}
/**
* @param string|list<string|null> $base
* @param array<string, array<string, string|array<string>>> $variants
* @param array<array<string, string|array<string>>> $compoundVariantsView on GitHub (pinned to a414c3a491)