twigphp/Twig · error · Twig\Error\RuntimeError

SeparatedTokenList can only be merged with iterables or…

Error message

SeparatedTokenList can only be merged with iterables or other SeparatedTokenList instances using the same separator.

What it means

SeparatedTokenList::mergeInto merges this list into a previous attribute value. The previous value must be iterable, or a SeparatedTokenList using the same separator; anything else (string, null, different-separator list) raises this RuntimeError to prevent silently mixing token lists with inconsistent separators.

Solutions

  1. Ensure the previous value is iterable or a SeparatedTokenList with the same separator.
  2. Normalize both lists to the same separator before merging.
  3. Convert a raw string into an array (explode by separator) before merging.

Example fix

// before
$merged = $list->mergeInto('a b c');
// after
$merged = $list->mergeInto(new SeparatedTokenList(' ', ['a','b','c']));
Defensive patterns

Strategy: type-guard

Validate before calling

$canMerge = $previous instanceof SeparatedTokenList
    ? $previous->getSeparator() === $list->getSeparator()
    : is_iterable($previous);
if (!$canMerge) {
    throw new InvalidArgumentException('Incompatible previous value for SeparatedTokenList merge');
}
$merged = $list->mergeInto($previous);

Type guard

function canMergeTokenList(mixed $previous, SeparatedTokenList $list): bool {
    return ($previous instanceof SeparatedTokenList && $previous->getSeparator() === $list->getSeparator())
        || is_iterable($previous);
}

Try / catch

try {
    $merged = $list->mergeInto($previous);
} catch (\Twig\Error\RuntimeError $e) {
    if (str_contains($e->getMessage(), 'same separator')) {
        // normalize separators, then retry
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling $list->mergeInto('a b c') with a plain string, mergeInto(null), or merging two SeparatedTokenList instances with different separators (e.g. ' ' vs ',').

Common situations: Merging into an already-rendered string attribute, or combining comma-separated lists with space-separated ones.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13). Data as JSON: /api/errors/7c178200c1938082. Report an issue: GitHub.

Appendix: source

Thrown at extra/html-extra/HtmlAttr/SeparatedTokenList.php:44

            $this->value = [...$value];
        } elseif (\is_scalar($value)) {
            $this->value = [$value];
        } else {
            throw new RuntimeError('SeparatedTokenList can only be constructed from iterable or scalar values.');
        }
    }

    public function mergeInto(mixed $previous): mixed
    {
        if ($previous instanceof self && $previous->separator === $this->separator) {
            return new self([...$previous->value, ...$this->value], $this->separator);
        }

        if (is_iterable($previous)) {
            return new self([...$previous, ...$this->value], $this->separator);
        }

        throw new RuntimeError('SeparatedTokenList can only be merged with iterables or other SeparatedTokenList instances using the same separator.');
    }

    public function appendFrom(mixed $newValue): mixed
    {
        if (!is_iterable($newValue)) {
            throw new RuntimeError('Only iterable values can be appended to SeparatedTokenList.');
        }

        return new self([...$this->value, ...$newValue], $this->separator);
    }

    public function getValue(): ?string
    {
        $filtered = array_filter($this->value, static fn ($v) => null !== $v && false !== $v);

        // Omit attribute if list contains only false or null values
        if (!$filtered) {
            return null;

View on GitHub (pinned to a414c3a491)