{"record":{"id":"95d4ac165c7f6376","repo":"phacility/phabricator","slug":"unable-to-build-resource-interface-of-type-s","errorCode":null,"errorMessage":"Unable to build resource interface of type \"%s\".","messagePattern":"Unable to build resource interface of type \"(.+?)\"\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/drydock/storage/DrydockBlueprint.php","lineNumber":272,"sourceCode":"    DrydockResource $resource,\n    DrydockLease $lease) {\n    $this->getImplementation()->destroyLease(\n      $this,\n      $resource,\n      $lease);\n    return $this;\n  }\n\n  public function getInterface(\n    DrydockResource $resource,\n    DrydockLease $lease,\n    $type) {\n\n    $interface = $this->getImplementation()\n      ->getInterface($this, $resource, $lease, $type);\n\n    if (!$interface) {\n      throw new Exception(\n        pht(\n          'Unable to build resource interface of type \"%s\".',\n          $type));\n    }\n\n    return $interface;\n  }\n\n  public function shouldAllocateSupplementalResource(\n    DrydockResource $resource,\n    DrydockLease $lease) {\n    return $this->getImplementation()->shouldAllocateSupplementalResource(\n      $this,\n      $resource,\n      $lease);\n  }\n\n","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/drydock/storage/DrydockBlueprint.php#L254-L290","documentation":"DrydockBlueprint::getInterface() delegates to the blueprint implementation (a DrydockBlueprintImplementation subclass) to construct a typed interface object (e.g. 'command' → command interface, 'wcmatches' style host interfaces) for a resource/lease pair. If the implementation returns null for the requested type, the storage layer throws, because downstream code cannot operate on a missing interface. It almost always means the blueprint's concrete class simply does not support that interface type for that resource type.","triggerScenarios":"Calling `$blueprint->getInterface($resource, $lease, 'command')` on a blueprint implementation whose getInterface() has no case for 'command'; asking for an interface type valid only after the resource is further configured; a third-party blueprint that forgets a default: null return; misspelled interface type string.","commonSituations":"Writing a custom Drydock blueprint (host blueprint, working-copy blueprint) and testing lease activation; consumers requesting 'command' interfaces from working-copy blueprints that only provide 'command' via their host lease instead; typos like 'commands' vs 'command'.","solutions":["Check the concrete DrydockBlueprintImplementation subclass's getInterface(): add/fill the case for the requested type and return a real interface object","Confirm you are asking the right blueprint: interface availability depends on the blueprint implementation, not just the resource","Verify the type string exactly matches the contract key (e.g. 'command'), no plural/typo","Trace callers of getInterface() on the lease/resource activation path to see which type is requested before the failure"],"exampleFix":"// before (custom blueprint implementation)\npublic function getInterface(\n  DrydockBlueprint $blueprint,\n  DrydockResource $resource,\n  DrydockLease $lease,\n  $type) {\n  return null; // no handling for $type\n}\n\n// after\npublic function getInterface(\n  DrydockBlueprint $blueprint,\n  DrydockResource $resource,\n  DrydockLease $lease,\n  $type) {\n  switch ($type) {\n    case 'command':\n      return id(new DrydockCommandInterface())\n        ->setLease($lease)\n        ->setResource($resource);\n  }\n  return null;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"// If your implementation exposes a capability list, check it first:\nif (!in_array($type, $blueprint_implementation->getInterfaceTypes(), true)) {\n  // request a different blueprint/lease rather than calling getInterface()\n}","tryCatchPattern":"try {\n  $interface = $blueprint->getInterface($resource, $lease, $type);\n} catch (Exception $ex) {\n  // getInterface() returned null for this type: the blueprint does not\n  // support it. Log $type + blueprint class and fail this lease's command\n  // with an actionable message; do not retry with the same blueprint/type.\n}","preventionTips":["Implement every interface type your consumers request in custom DrydockBlueprintImplementation subclasses, and return real objects","Keep a canonical list of interface type strings and assert against it on both producer and consumer sides","Test custom blueprints by acquiring a lease and requesting each supported interface in a unit/integration test"],"tags":["drydock","phabricator","blueprint","interface","null-return","custom-blueprint"],"backgroundTag":"interface-not-implemented","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}