{"record":{"id":"269741c14d056060","repo":"phacility/phabricator","slug":"curtain-extension-s-of-class-s-did-not-re","errorCode":null,"errorMessage":"Curtain extension (\"%s\", of class \"%s\") did not return an application from method \"%s\". This method must return an object of class \"%s\".","messagePattern":"Curtain extension \\(\"(.+?)\", of class \"(.+?)\"\\) did not return an application from method \"(.+?)\"\\. This method must return an object of class \"(.+?)\"\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/view/extension/PHUICurtainExtension.php","lineNumber":60,"sourceCode":"  }\n\n  protected function newPanel() {\n    return new PHUICurtainPanelView();\n  }\n\n  final public static function buildExtensionPanels(\n    PhabricatorUser $viewer,\n    $object) {\n\n    $extensions = self::getAllExtensions();\n    foreach ($extensions as $extension) {\n      $extension->setViewer($viewer);\n    }\n\n    foreach ($extensions as $key => $extension) {\n      $application = $extension->getExtensionApplication();\n      if (!($application instanceof PhabricatorApplication)) {\n        throw new Exception(\n          pht(\n            'Curtain extension (\"%s\", of class \"%s\") did not return an '.\n            'application from method \"%s\". This method must return an '.\n            'object of class \"%s\".',\n            $key,\n            get_class($extension),\n            'getExtensionApplication()',\n            'PhabricatorApplication'));\n      }\n\n      $has_application = PhabricatorApplication::isClassInstalledForViewer(\n        get_class($application),\n        $viewer);\n\n      if (!$has_application) {\n        unset($extensions[$key]);\n      }\n    }","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/view/extension/PHUICurtainExtension.php#L42-L78","documentation":"Every PHUICurtainExtension subclass must implement getExtensionApplication() and return the PhabricatorApplication it belongs to. PHUICurtainExtension::buildExtensionPanels() uses that object to test whether the application is installed and visible for the current viewer before building any curtain panels. If the method returns null, a class-name string, or any non-PhabricatorApplication value, this exception is thrown on every page render that builds curtains.","triggerScenarios":"A custom curtain extension implements buildCurtainPanels() but leaves getExtensionApplication() returning null; or returns the string 'PhabricatorManiphestApplication' instead of an application instance; or returns the result of a lookup that can be null.","commonSituations":"Writing a first custom extension by copying only the panel-building half from a core extension; refactoring or renaming an application class so the returned class no longer exists; fork updates that change application constructors.","solutions":["Implement getExtensionApplication() to return a real instance, e.g. return new PhabricatorManiphestApplication();","Verify the returned class is a concrete PhabricatorApplication subclass that exists and is enabled.","Add a unit test that calls PHUICurtainExtension::buildExtensionPanels($viewer, $object) so contract violations fail in CI."],"exampleFix":"// before\npublic function getExtensionApplication() {\n  return null; // or 'PhabricatorManiphestApplication'\n}\n\n// after\npublic function getExtensionApplication() {\n  return new PhabricatorManiphestApplication();\n}","handlingStrategy":"type-guard","validationCode":"$app = $extension->getExtensionApplication();\nif (!($app instanceof PhabricatorApplication)) {\n  // contract violation: fix the extension before it renders in production\n  phlog(pht('Bad getExtensionApplication() in %s', get_class($extension)));\n}","typeGuard":"function isValidCurtainExtension(PHUICurtainExtension $extension) {\n  return $extension->getExtensionApplication()\n    instanceof PhabricatorApplication;\n}","tryCatchPattern":null,"preventionTips":["Model new extensions on core ones (e.g. Maniphest curtain extensions) that return a real application object.","Never return a class-name string or null from getExtensionApplication().","Run buildExtensionPanels() in a unit test for each custom extension."],"tags":["php","phabricator","curtain-extension","return-type","application"],"backgroundTag":"extension-contract-violation","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}