{"record":{"id":"dbf71b9ad9bedbab","repo":"yiisoft/yii2","slug":"expecting-end-of-widget-found-calledclass","errorCode":null,"errorMessage":"Expecting end() of {widget}, found {calledClass}","messagePattern":"Expecting end\\(\\) of (.+?), found (.+?)","errorType":"exception","errorClass":"InvalidCallException","httpStatus":null,"severity":"error","filePath":"framework/base/Widget.php","lineNumber":125,"sourceCode":"    public static function end()\n    {\n        if (!empty(self::$stack)) {\n            $widget = array_pop(self::$stack);\n\n            $calledClass = self::$_resolvedClasses[get_called_class()] ?? get_called_class();\n\n            if (get_class($widget) === $calledClass) {\n                /** @var static $widget */\n                if ($widget->beforeRun()) {\n                    $result = $widget->run();\n                    $result = $widget->afterRun($result);\n                    echo $result;\n                }\n\n                return $widget;\n            }\n\n            throw new InvalidCallException('Expecting end() of ' . get_class($widget) . ', found ' . get_called_class());\n        }\n\n        throw new InvalidCallException('Unexpected ' . get_called_class() . '::end() call. A matching begin() is not found.');\n    }\n\n    /**\n     * Creates a widget instance and runs it.\n     * The widget rendering result is returned by this method.\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     * @return string the rendering result of the widget.\n     * @throws \\Throwable\n     */\n    public static function widget($config = [])\n    {\n        ob_start();\n        ob_implicit_flush(false);\n        try {\n            $config['class'] = get_called_class();","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/base/Widget.php#L107-L143","documentation":"Thrown by yii\\base\\Widget::end() when the widget popped off the static stack is not the class on which end() was called. begin() pushes the created widget onto Widget::$stack and end() pops the last one; fluent widget configs can make end() resolve to the resolved class via $_resolvedClasses, so a mismatch means begin()/end() calls are improperly nested or interleaved between different widget classes.","triggerScenarios":"WidgetA::begin() ... WidgetB::end(); nesting two widgets and closing the outer one first (begin A, begin B, end A, end B); copy-pasting a block and changing only the end() class name; a missing end() for the inner widget so the outer end() pops the wrong instance; calling begin() conditionally but end() unconditionally is the sibling case.","commonSituations":"Hand-edited view layouts mixing widget wrappers (e.g. yii\\bootstrap\\ActiveForm vs yii\\widgets\\ActiveForm end() calls swapped); long view files where an inner widget's end() was accidentally deleted; merging template branches and losing one end(); inheritance where begin() was called on a parent alias but end() on a subclass with a different resolved class.","solutions":["Read the message: it names the widget actually open (get_class($widget)) and the class you tried to end — make them match.","Audit the view from the reported position: every Xxx::begin() must have a matching Xxx::end() in LIFO order, with inner widgets closed first.","Prefer the one-shot Widget::widget([...]) for widgets that do not need content captured between begin and end.","Enable view linting/IDE inspections that pair begin/end calls, and keep widget blocks short."],"exampleFix":"// before\nActiveForm::begin();\nPanel::begin();\n    echo $content;\nActiveForm::end(); // throws: open widget is Panel, not ActiveForm\nPanel::end();\n\n// after\nActiveForm::begin();\nPanel::begin();\n    echo $content;\nPanel::end(); // close inner widget first\nActiveForm::end();","handlingStrategy":"validation","validationCode":"// Widget::$stack is public static — inspect it before end()\n$stack = \\yii\\base\\Widget::$stack;\nif (empty($stack) || !(end($stack) instanceof \\yii\\widgets\\ActiveForm)) {\n    // pairing bug: do not call ActiveForm::end() here\n    \\Yii::warning('ActiveForm::end() skipped: widget stack mismatch', 'widgets');\n    return;\n}\n\\yii\\widgets\\ActiveForm::end();","typeGuard":null,"tryCatchPattern":"try {\n    Panel::end();\n} catch (\\yii\\base\\InvalidCallException $e) {\n    // message names both classes; repair stack/flags and rethrow in debug\n    \\Yii::error('Widget pairing broken: ' . $e->getMessage(), 'widgets');\n    throw $e;\n}","preventionTips":["Keep each begin()/end() pair in the same view file and visually adjacent (LIFO order)","Use Widget::widget([...]) for anything that does not wrap content","Search views for '::end()' without '::begin()' during code review; many IDEs can pair them"],"tags":["php","yii2","widget","views","rendering","nesting"],"backgroundTag":"unbalanced-begin-end-calls","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}