twigphp/Twig · error · TypeError
Block chain templates must be strings or
Error message
Block chain templates must be strings or "%s" instances, "%s" given.
What it means
BlockChain's constructor accepts an iterable of templates, each either a string template name or a TemplateWrapper. If an element is neither (e.g. a raw Template object, null, or an array), a TypeError is thrown reporting the actual debug type via get_debug_type(). The chain needs wrappers it can unwrap and verify against the Environment.
Solutions
- Pass template name strings (they are loaded via $env->load internally) or TemplateWrapper instances from Environment::load().
- If you only have a Template, load its wrapper via the environment instead of passing the raw Template.
- Add a per-item check with instanceof TemplateWrapper before constructing the chain.
Example fix
// before
$chain = new \Twig\BlockChain($env, [$env->resolveTemplate('a.html.twig')]); // raw Template
// after
$chain = new \Twig\BlockChain($env, ['a.html.twig']); // or [$env->load('a.html.twig')] Defensive patterns
Strategy: type-guard
Validate before calling
foreach ($templates as $t) {
if (!is_string($t) && !$t instanceof \Twig\TemplateWrapper) {
throw new \InvalidArgumentException(sprintf('Expected string or TemplateWrapper, got %s.', get_debug_type($t)));
}
} Type guard
function isChainTemplate(mixed $t): bool {
return is_string($t) || $t instanceof \Twig\TemplateWrapper;
} Try / catch
try {
$chain = new \Twig\BlockChain($env, $templates);
} catch (\TypeError $e) {
// normalize: map non-string entries through $env->load() or drop them
} Prevention
- Use Environment::load() / template name strings, never raw \Twig\Template objects.
- Do not call ->unwrap() before constructing the chain.
- Type-hint collections as list<string|TemplateWrapper> in your own APIs feeding BlockChain.
When it happens
Trigger: Passing a \Twig\Template instance directly instead of a TemplateWrapper, passing null/false, or iterating a list that includes non-template values into Twig\BlockChain::__construct.
Common situations: Calling ->unwrap() yourself before passing to BlockChain, or mixing results from $env->resolveTemplate() (returns Template) with $env->load() (returns TemplateWrapper).
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 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…
- Only iterable values can be appended to SeparatedTokenList.
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/517200b2f05f89c2.
Report an issue: GitHub.
Appendix: source
Thrown at src/BlockChain.php:49
* Whether the lineage can no longer move, making every further resolution pointless.
*/
private bool $fixed = false;
/**
* @param iterable<string|TemplateWrapper> $templates Templates ordered from highest to lowest precedence
* @param array<string, mixed> $context Default variables used to resolve dynamic parent expressions
*/
public function __construct(
private Environment $env,
iterable $templates,
private array $context = [],
) {
foreach ($templates as $template) {
if (\is_string($template)) {
$template = $env->load($template);
}
if (!$template instanceof TemplateWrapper) {
throw new \TypeError(\sprintf('Block chain templates must be strings or "%s" instances, "%s" given.', TemplateWrapper::class, get_debug_type($template)));
}
$template = $template->unwrap();
if (!$template->isOwnedBy($env)) {
throw new \LogicException('A block chain cannot contain templates from different Twig environments.');
}
$this->templates[] = $template;
}
if (!$this->templates) {
throw new \InvalidArgumentException('A block chain requires at least one template.');
}
}
/**
* @param array<string, mixed> $context
*/View on GitHub (pinned to a414c3a491)