{"record":{"id":"6671af3f1cb04ed7","repo":"phacility/phabricator","slug":"curtain-extension-s-of-class-s-returned-a","errorCode":null,"errorMessage":"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.","messagePattern":"Curtain extension \\(\"(.+?)\", of class \"(.+?)\"\\) returned a list of curtain panels from \"(.+?)\" that contains an invalid value: a value \\(with key \"(.+?)\"\\) is not an object of class \"(.+?)\"\\. Each item in the returned array must be a panel\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/view/extension/PHUICurtainExtension.php","lineNumber":104,"sourceCode":"    $result = array();\n\n    foreach ($extensions as $key => $extension) {\n      $panels = $extension->buildCurtainPanels($object);\n      if (!is_array($panels)) {\n        throw new Exception(\n          pht(\n            'Curtain extension (\"%s\", of class \"%s\") did not return a list of '.\n            'curtain panels from method \"%s\". This method must return an '.\n            'array, and each value in the array must be a \"%s\" object.',\n            $key,\n            get_class($extension),\n            'buildCurtainPanels()',\n            'PHUICurtainPanelView'));\n      }\n\n      foreach ($panels as $panel_key => $panel) {\n        if (!($panel instanceof PHUICurtainPanelView)) {\n          throw new Exception(\n            pht(\n              'Curtain extension (\"%s\", of class \"%s\") returned a list of '.\n              'curtain panels from \"%s\" that contains an invalid value: '.\n              'a value (with key \"%s\") is not an object of class \"%s\". '.\n              'Each item in the returned array must be a panel.',\n              $key,\n              get_class($extension),\n              'buildCurtainPanels()',\n              $panel_key,\n              'PHUICurtainPanelView'));\n        }\n\n        $result[] = $panel;\n      }\n    }\n\n    return $result;\n  }","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/view/extension/PHUICurtainExtension.php#L86-L122","documentation":"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.","triggerScenarios":"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(...).","commonSituations":"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.","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."],"exampleFix":"// before\npublic function buildCurtainPanels($object) {\n  return array(\n    $this->newInfoView($object), // not a PHUICurtainPanelView: throws\n  );\n}\n\n// after\npublic function buildCurtainPanels($object) {\n  $panel = id(new PHUICurtainPanelView())\n    ->setHeaderText(pht('Details'))\n    ->appendChild($this->newInfoView($object));\n  return array($panel);\n}","handlingStrategy":"type-guard","validationCode":"foreach ($extension->buildCurtainPanels($object) as $panel) {\n  if (!($panel instanceof PHUICurtainPanelView)) {\n    // non-panel value: fix the extension before production rendering\n    phlog(pht('Non-panel value returned by %s', get_class($extension)));\n  }\n}","typeGuard":"function isPanelList(array $panels) {\n  foreach ($panels as $panel) {\n    if (!($panel instanceof PHUICurtainPanelView)) {\n      return false;\n    }\n  }\n  return true;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["php","phabricator","curtain-extension","instanceof","render"],"backgroundTag":"extension-contract-violation","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}