octobercms/october · error · RuntimeException

Cannot capture slot '{$name}': no active component.

Error message

Cannot capture slot '{$name}': no active component.

What it means

ViewComponent powers the UiFactory fluent UI API: `UiFactory::card(...)->slot()` pushes a component onto a static stack and opens an output buffer; captureSlot('name') then switches the captured slot. captureSlot() uses end(static::$stack) and throws a RuntimeException when the stack is empty, because a slot has to belong to some active component. So this error means captureSlot() ran before any ->slot() started a component, or after endComponent() already closed the last one.

Source

Thrown at modules/system/classes/ViewComponent.php:80

    /**
     * slot starts capturing content for a named slot
     */
    public function slot(string $name = 'default'): void
    {
        ob_start();
        $this->currentSlot = $name;
        static::$stack[] = $this;
    }

    /**
     * captureSlot switches to a new named slot, closing the previous one
     */
    public static function captureSlot(string $name): void
    {
        $instance = end(static::$stack);

        if ($instance === false) {
            throw new \RuntimeException("Cannot capture slot '{$name}': no active component.");
        }

        // 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);

View on GitHub (pinned to b608633a7e)

Solutions

  1. Start a component first: `UiFactory::card(...)->slot()` (or equivalent), then captureSlot('name'), then UiFactory::end()
  2. Check the template for a deleted or commented-out `->slot()` opening line above the captureSlot call
  3. Make sure conditionals wrap whole open…close blocks, never just the opening half
  4. Match every UiFactory::end() with exactly one ->slot() start in the same branch

Example fix

// before — captureSlot with no component opened
<?php UiFactory::captureSlot('header') ?>
    <h1>Hi</h1>
<?php UiFactory::end() ?>
// after — open the component (and default slot) first
<?php UiFactory::card(title: 'Panel')->slot() ?>
    <p>Body</p>
    <?php UiFactory::captureSlot('header') ?>
        <h1>Hi</h1>
    <?php UiFactory::captureSlot('footer') ?>
        <small>Bye</small>
<?php UiFactory::end() ?>
Defensive patterns

Strategy: type-guard

Type guard

function hasActiveViewComponent(): bool
{
    $prop = new ReflectionProperty(\System\Classes\ViewComponent::class, 'stack');
    return !empty($prop->getValue());
}

// UiFactory::captureSlot('header') only when a component is open:
if (hasActiveViewComponent()) {
    \System\Classes\ViewComponent::captureSlot('header');
}

Prevention

When it happens

Trigger: Calling ViewComponent::captureSlot('x') / UiFactory::captureSlot('x') in a view without a preceding `->slot()` call on a component; calling it after `UiFactory::end()` closed the only component; copy-pasting a slot block outside its component wrapper.

Common situations: Refactoring a UI template and moving a captureSlot block out of its component; nesting mistakes where the opening `->slot()` line was deleted; using conditional rendering that skips the open but keeps the captureSlot.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/f861eefba35c4a68. Report an issue: GitHub.