{"record":{"id":"14263ff864d9d2be","repo":"twigphp/Twig","slug":"only-iterable-values-can-be-appended-to-separatedtokenlist","errorCode":null,"errorMessage":"Only iterable values can be appended to SeparatedTokenList.","messagePattern":"Only iterable values can be appended to SeparatedTokenList\\.","errorType":"exception","errorClass":"Twig\\Error\\RuntimeError","httpStatus":null,"severity":"error","filePath":"extra/html-extra/HtmlAttr/SeparatedTokenList.php","lineNumber":50,"sourceCode":"    }\n\n    public function mergeInto(mixed $previous): mixed\n    {\n        if ($previous instanceof self && $previous->separator === $this->separator) {\n            return new self([...$previous->value, ...$this->value], $this->separator);\n        }\n\n        if (is_iterable($previous)) {\n            return new self([...$previous, ...$this->value], $this->separator);\n        }\n\n        throw new RuntimeError('SeparatedTokenList can only be merged with iterables or other SeparatedTokenList instances using the same separator.');\n    }\n\n    public function appendFrom(mixed $newValue): mixed\n    {\n        if (!is_iterable($newValue)) {\n            throw new RuntimeError('Only iterable values can be appended to SeparatedTokenList.');\n        }\n\n        return new self([...$this->value, ...$newValue], $this->separator);\n    }\n\n    public function getValue(): ?string\n    {\n        $filtered = array_filter($this->value, static fn ($v) => null !== $v && false !== $v);\n\n        // Omit attribute if list contains only false or null values\n        if (!$filtered) {\n            return null;\n        }\n\n        // true values are not printed, but result in the attribute not being omitted\n        return trim(implode($this->separator, array_filter($filtered, static fn ($v) => true !== $v)));\n    }\n}","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/extra/html-extra/HtmlAttr/SeparatedTokenList.php#L32-L68","documentation":"SeparatedTokenList::appendFrom adds new tokens to this list; the new value must be iterable so it can be spread into the internal token array. Non-iterable values (string, null, scalar) raise this RuntimeError. Note the constructor accepts scalars but appendFrom does not — wrap a single token in an array.","triggerScenarios":"Calling $list->appendFrom('extra-class') with a raw string, appendFrom(null), or appendFrom(42).","commonSituations":"Appending a single class name string instead of an array of class names.","solutions":["Wrap the token in an array: appendFrom(['extra-class']).","Append an array/Traversable of tokens.","If appending another list, use a SeparatedTokenList with the same separator and merge/append its value array."],"exampleFix":"// before\n$list->appendFrom('extra-class');\n// after\n$list->appendFrom(['extra-class']);","handlingStrategy":"type-guard","validationCode":"if (!is_iterable($newValue)) {\n    $newValue = [$newValue];\n}\n$list->appendFrom($newValue);","typeGuard":"function asTokenIterable(mixed $v): array {\n    return is_iterable($v) ? [...$v] : [$v];\n}","tryCatchPattern":"try {\n    $list->appendFrom($newValue);\n} catch (\\Twig\\Error\\RuntimeError $e) {\n    if (str_contains($e->getMessage(), 'can be appended to SeparatedTokenList')) {\n        $list->appendFrom([$newValue]);\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Always wrap single tokens in an array before appendFrom","Remember appendFrom is stricter than the constructor (no scalars)","Centralize list appends in a helper that normalizes to array"],"tags":["twig","html-extra","separated-token-list","append","type-error"],"backgroundTag":"type-mismatch","analyzedSha":"a414c3a491defb5a60f2fc88ef79ff37c90010cd","analyzedAt":"2026-09-13T15:10:46.849Z","contentChangedAt":"2026-09-13T15:10:46.849Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}