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

  1. Wrap every element in a panel: id(new PHUICurtainPanelView())->setHeaderText(...)->appendChild($content).
  2. Filter skipped entries before returning so null never lands in the array (array_filter or conditional building).
  3. 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

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


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/6671af3f1cb04ed7. Report an issue: GitHub.