phacility/phabricator · error · Exception
Curtain extension ("%s", of class "%s") returned a list of c
Error message
Curtain extension ("%s", of class "%s") returned a list of curtain panels from "%s" that contains an invalid value: a value (with key "%s") is not an object of class "%s". Each item in the returned array must be a panel. What it means
Each element of the array returned by buildCurtainPanels() must be a PHUICurtainPanelView instance. After checking that the return value is an array, buildExtensionPanels() verifies every element with instanceof and throws on the first non-panel value, naming the offending array key in the message. Raw HTML strings, other view types, and null placeholders all fail this check.
Source
Thrown at src/view/extension/PHUICurtainExtension.php:104
$result = array();
foreach ($extensions as $key => $extension) {
$panels = $extension->buildCurtainPanels($object);
if (!is_array($panels)) {
throw new Exception(
pht(
'Curtain extension ("%s", of class "%s") did not return a list of '.
'curtain panels from method "%s". This method must return an '.
'array, and each value in the array must be a "%s" object.',
$key,
get_class($extension),
'buildCurtainPanels()',
'PHUICurtainPanelView'));
}
foreach ($panels as $panel_key => $panel) {
if (!($panel instanceof PHUICurtainPanelView)) {
throw new Exception(
pht(
'Curtain extension ("%s", of class "%s") returned a list of '.
'curtain panels from "%s" that contains an invalid value: '.
'a value (with key "%s") is not an object of class "%s". '.
'Each item in the returned array must be a panel.',
$key,
get_class($extension),
'buildCurtainPanels()',
$panel_key,
'PHUICurtainPanelView'));
}
$result[] = $panel;
}
}
return $result;
}View on GitHub (pinned to 5720a38cfe)
Solutions
- Wrap every element in a panel: id(new PHUICurtainPanelView())->setHeaderText(...)->appendChild($content).
- Filter skipped entries before returning so null never lands in the array (array_filter or conditional building).
- Use the array key named in the exception message to locate the exact offending element quickly.
Example fix
// before
public function buildCurtainPanels($object) {
return array(
$this->newInfoView($object), // not a PHUICurtainPanelView: throws
);
}
// after
public function buildCurtainPanels($object) {
$panel = id(new PHUICurtainPanelView())
->setHeaderText(pht('Details'))
->appendChild($this->newInfoView($object));
return array($panel);
} Defensive patterns
Strategy: type-guard
Validate before calling
foreach ($extension->buildCurtainPanels($object) as $panel) {
if (!($panel instanceof PHUICurtainPanelView)) {
// non-panel value: fix the extension before production rendering
phlog(pht('Non-panel value returned by %s', get_class($extension)));
}
} Type guard
function isPanelList(array $panels) {
foreach ($panels as $panel) {
if (!($panel instanceof PHUICurtainPanelView)) {
return false;
}
}
return true;
} Prevention
- Only PHUICurtainPanelView objects may enter the returned array.
- Wrap arbitrary content with setHeaderText()/appendChild() on a panel; never push strings or other views.
- Filter out skipped entries so null never appears in the list.
When it happens
Trigger: buildCurtainPanels() returns array($html_string), array(id(new PHUIInfoView())), or contains null entries from conditional building, instead of wrapping content inside id(new PHUICurtainPanelView())->appendChild(...).
Common situations: Returning rendered HTML or arbitrary views where panels are required; pushing null placeholders for skipped entries; migrating code that previously produced a list of generic renderable elements.
Related errors
- Curtain extension ("%s", of class "%s") did not return a lis
- View '%s' did not return an array from getTagAttributes()!
- Curtain extension ("%s", of class "%s") did not return an ap
- You must set the %s to render a %s.
- Service "%s" is unrecognized, restricted, or you do not have
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/6671af3f1cb04ed7.
Report an issue: GitHub.