octobercms/october · error · RuntimeException
Cannot end component: no active component.
Error message
Cannot end component: no active component.
What it means
ViewComponent::endComponent() (exposed as UiFactory::end()) pops the last component off the static stack, captures its final slot, and echoes the rendered result. When array_pop() returns null the stack was already empty, and it throws a RuntimeException: more end() calls than component starts in the current render. Typical markup imbalance — an extra @end / UiFactory::end(), or an end() outside a condition whose matching ->slot() open never ran.
Source
Thrown at modules/system/classes/ViewComponent.php:101
// Close previous slot
if ($instance->currentSlot !== null) {
$instance->slots[$instance->currentSlot] = ob_get_clean();
}
// Start new slot
ob_start();
$instance->currentSlot = $name;
}
/**
* endComponent captures the final slot and renders the component
*/
public static function endComponent(): void
{
$instance = array_pop(static::$stack);
if ($instance === null) {
throw new \RuntimeException("Cannot end component: no active component.");
}
// Capture final slot
if ($instance->currentSlot !== null) {
$instance->slots[$instance->currentSlot] = ob_get_clean();
}
echo $instance->render();
}
/**
* __call supports fluent API for setting props
*/
public function __call(string $method, array $args): static
{
$this->props[$method] = $args[0] ?? true;
return $this;
}
View on GitHub (pinned to b608633a7e)
Solutions
- Count `->slot()` opens versus UiFactory::end() closes in the failing template and its partials — they must pair 1:1 on every code path
- Check conditionals/loops: each branch that renders an end() must also have rendered the open in the same branch
- Remove stray end() calls duplicated by includes/partials
- Prefer the inline `<?= UiFactory::button(...) ?>` form for simple components to avoid manual pairing entirely
Example fix
// before — extra end() with no matching open
<?php UiFactory::card(title: 'A')->slot() ?>
<p>One</p>
<?php UiFactory::end() ?>
<?php UiFactory::end() ?> <!-- RuntimeException: no active component -->
// after — one open, one close
<?php UiFactory::card(title: 'A')->slot() ?>
<p>One</p>
<?php UiFactory::end() ?> Defensive patterns
Strategy: type-guard
Type guard
function hasOpenViewComponent(): bool
{
$prop = new ReflectionProperty(\System\Classes\ViewComponent::class, 'stack');
return !empty($prop->getValue());
}
if (hasOpenViewComponent()) {
\System\Classes\ViewComponent::endComponent();
} else {
throw new LogicException('Unbalanced UiFactory template: extra end()');
} Prevention
- Keep component open/close blocks contiguous — no interleaved partials that own the other half
- Wrap whole components in single if/else branches, never split across them
- Code-review copy-pasted partials for duplicated end() calls
When it happens
Trigger: An extra UiFactory::end() in a template; an end() in an else-branch while the ->slot() open was in the if-branch; duplicated footer/partial that also contains an end(); calling endComponent() directly without ever calling ->slot().
Common situations: Copy-pasted partials that each carry their own end(); refactoring that leaves orphan close tags; conditionals that pair opens and closes asymmetrically.
Related errors
- Cannot capture slot '{$name}': no active component.
- cms::lang.component.not_found
- Component not found
- cms::lang.component.no_records
- cms::lang.component.not_found
AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21).
Data as JSON: /api/errors/d1504e2b604ff3d1.
Report an issue: GitHub.