{"record":{"id":"9496e5ba0b759a0f","repo":"phacility/phabricator","slug":"trying-to-acquire-a-lease-on-a-resource-which-is-i","errorCode":null,"errorMessage":"Trying to acquire a lease on a resource which is in the wrong state: status must be \"%s\", actually \"%s\".","messagePattern":"Trying to acquire a lease on a resource which is in the wrong state: status must be \"(.+?)\", actually \"(.+?)\"\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/drydock/storage/DrydockLease.php","lineNumber":220,"sourceCode":"\n    return $this;\n  }\n\n  public function setActivateWhenAcquired($activate) {\n    $this->activateWhenAcquired = true;\n    return $this;\n  }\n\n  public function needSlotLock($key) {\n    $this->slotLocks[] = $key;\n    return $this;\n  }\n\n  public function acquireOnResource(DrydockResource $resource) {\n    $expect_status = DrydockLeaseStatus::STATUS_PENDING;\n    $actual_status = $this->getStatus();\n    if ($actual_status != $expect_status) {\n      throw new Exception(\n        pht(\n          'Trying to acquire a lease on a resource which is in the wrong '.\n          'state: status must be \"%s\", actually \"%s\".',\n          $expect_status,\n          $actual_status));\n    }\n\n    if ($this->activateWhenAcquired) {\n      $new_status = DrydockLeaseStatus::STATUS_ACTIVE;\n    } else {\n      $new_status = DrydockLeaseStatus::STATUS_ACQUIRED;\n    }\n\n    if ($new_status == DrydockLeaseStatus::STATUS_ACTIVE) {\n      if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) {\n        throw new Exception(\n          pht(\n            'Trying to acquire an active lease on a pending resource. '.","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/drydock/storage/DrydockLease.php#L202-L238","documentation":"DrydockLease::acquireOnResource() is the single entry point that moves a lease from PENDING onto a resource, and its precondition is that the lease status is still STATUS_PENDING. Any other status (RELEASED, ACTIVE, ACQUIRED, BROKEN...) means the lease already went through (or skipped) part of its lifecycle, so the state machine refuses with this Exception. This is a programmer/state-machine error, not a transient condition — retrying the same call on the same lease object will fail identically.","triggerScenarios":"Calling acquireOnResource() twice on the same lease object (double allocation in custom allocator code); acquiring a lease that a worker already acquired concurrently; acquiring a lease loaded in a stale state after another process released it.","commonSituations":"Custom blueprint implementations or Drydock extensions that hand-roll the allocation sequence; retry logic that re-enters acquire with the same lease after a partial failure; reusing a lease object fetched before a daemon processed it.","solutions":["Reload the lease from the database immediately before acquiring and confirm status is still DrydockLeaseStatus::STATUS_PENDING","Make sure you acquire each lease exactly once — follow the pattern in DrydockLeaseUpdateWorker::acquireLease()","If the lease already moved to ACQUIRED, call activateOnResource() instead; if RELEASED/BROKEN, create a new lease","Guard custom allocator code against double-entry (yield/retry with a fresh load rather than reusing the in-memory object)"],"exampleFix":"// before\n$lease->acquireOnResource($resource);\n// possibly: status must be \"pending\", actually \"acquired\"\n\n// after\n$lease = id(new DrydockLeaseQuery())\n  ->setViewer($viewer)\n  ->withPHIDs(array($lease->getPHID()))\n  ->executeOne();\nif ($lease->getStatus() !== DrydockLeaseStatus::STATUS_PENDING) {\n  return; // already advanced by another worker\n}\n$lease->acquireOnResource($resource);","handlingStrategy":"validation","validationCode":"// Assert lease is still PENDING immediately before acquiring:\n$lease = id(new DrydockLeaseQuery())\n  ->setViewer($viewer)\n  ->withPHIDs(array($lease->getPHID()))\n  ->executeOne();\nif ($lease->getStatus() !== DrydockLeaseStatus::STATUS_PENDING) {\n  // another worker advanced it; skip, don't acquire\n}\n$lease->acquireOnResource($resource);","typeGuard":null,"tryCatchPattern":"catch (Exception $ex) { on 'status must be \"pending\"', reload the lease: if ACQUIRED continue to activation, if ACTIVE it is done, otherwise (RELEASED/BROKEN) abandon and create a new lease }","preventionTips":["Never call acquireOnResource() twice on the same lease; model retry as reload-then-decide","Follow the worker's acquireLease() pattern in custom allocator code","Keep allocation decisions on fresh database loads, not cached objects, in concurrent contexts"],"tags":["drydock","phabricator","lease","state-machine","double-acquire","stale-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}