{"record":{"id":"fd2282ec2e5c9c7a","repo":"phacility/phabricator","slug":"blueprint-s-of-type-s-is-not-properly-impl-fd2282","errorCode":null,"errorMessage":"Blueprint \"%s\" (of type \"%s\") is not properly implemented: %s must actually allocate the resource it returns.","messagePattern":"Blueprint \"(.+?)\" \\(of type \"(.+?)\"\\) is not properly implemented: (.+?) must actually allocate the resource it returns\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/drydock/worker/DrydockResourceUpdateWorker.php","lineNumber":189,"sourceCode":"    $blueprint->activateResource($resource);\n    $this->validateActivatedResource($blueprint, $resource);\n\n    $awaken_ids = $this->getTaskDataValue('awakenOnActivation');\n    if (is_array($awaken_ids) && $awaken_ids) {\n      PhabricatorWorker::awakenTaskIDs($awaken_ids);\n    }\n  }\n\n\n  /**\n   * @task activate\n   */\n  private function validateActivatedResource(\n    DrydockBlueprint $blueprint,\n    DrydockResource $resource) {\n\n    if (!$resource->isActivatedResource()) {\n      throw new Exception(\n        pht(\n          'Blueprint \"%s\" (of type \"%s\") is not properly implemented: %s '.\n          'must actually allocate the resource it returns.',\n          $blueprint->getBlueprintName(),\n          $blueprint->getClassName(),\n          'allocateResource()'));\n    }\n\n  }\n\n\n/* -(  Releasing Resources  )------------------------------------------------ */\n\n\n  /**\n   * @task release\n   */\n  private function releaseResource(","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/drydock/worker/DrydockResourceUpdateWorker.php#L171-L207","documentation":"DrydockResourceUpdateWorker::activateResource() calls the blueprint's activateResource() and then verifies the resource actually became active (isActivatedResource() is set by DrydockResource::activateResource()). If the blueprint returned without activating, this exception fires. Note the message text is a copy of the allocation validator ('allocateResource()') even though it guards activation, which can mislead; the fix is in the blueprint's activateResource().","triggerScenarios":"Custom blueprint activateResource() that does provisioning work but never calls $resource->activateResource(); calling it conditionally and returning on a skipped branch.","commonSituations":"Writing the activation step of a custom blueprint; porting a blueprint written before explicit activation existed; being misled by the 'allocateResource()' text in the message into fixing the wrong method.","solutions":["End the blueprint's activateResource() with $resource->activateResource() on every success path.","Throw on failure paths instead of returning quietly.","Ignore the misleading 'allocateResource()' wording in the message; the validator runs after activation, not allocation."],"exampleFix":"// before\npublic function activateResource(DrydockResource $resource) {\n  $this->provisionStorage($resource);\n  // missing $resource->activateResource()\n}\n\n// after\npublic function activateResource(DrydockResource $resource) {\n  $this->provisionStorage($resource);\n  $resource->activateResource();\n}","handlingStrategy":"validation","validationCode":"if (!$resource->isActivatedResource()) {\n  throw new Exception(\n    pht('activateResource() returned without activating; call $resource->activateResource().'));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["End blueprint activateResource() with $resource->activateResource() on every success path.","Ignore the 'allocateResource()' wording in the message; this validator runs after activation."],"tags":["drydock","php","blueprint","contract","resource-lifecycle","state-machine"],"backgroundTag":"invalid-state-transition","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}