{"record":{"id":"f050f4888f066c44","repo":"phacility/phabricator","slug":"trying-to-activate-a-lease-which-has-the-wrong-sta","errorCode":null,"errorMessage":"Trying to activate a lease which has the wrong status: status must be \"%s\", actually \"%s\".","messagePattern":"Trying to activate a lease which has the wrong status: status must be \"(.+?)\", actually \"(.+?)\"\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/drydock/storage/DrydockLease.php","lineNumber":333,"sourceCode":"\n    $this->logEvent(DrydockLeaseAcquiredLogType::LOGCONST);\n\n    if ($new_status == DrydockLeaseStatus::STATUS_ACTIVE) {\n      $this->didActivate();\n    }\n\n    return $this;\n  }\n\n  public function isAcquiredLease() {\n    return $this->isAcquired;\n  }\n\n  public function activateOnResource(DrydockResource $resource) {\n    $expect_status = DrydockLeaseStatus::STATUS_ACQUIRED;\n    $actual_status = $this->getStatus();\n    if ($actual_status != $expect_status) {\n      throw new Exception(\n        pht(\n          'Trying to activate a lease which has the wrong status: status '.\n          'must be \"%s\", actually \"%s\".',\n          $expect_status,\n          $actual_status));\n    }\n\n    if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) {\n      // TODO: Be stricter about this?\n      throw new Exception(\n        pht(\n          'Trying to activate a lease on a pending resource.'));\n    }\n\n    $this->openTransaction();\n\n    try {\n      DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/drydock/storage/DrydockLease.php#L315-L351","documentation":"DrydockLease::activateOnResource() performs the ACQUIRED → ACTIVE transition, so it hard-requires the lease to currently be in STATUS_ACQUIRED. If the lease is still PENDING (acquisition never happened or has not committed), already ACTIVE, or RELEASED/BROKEN, activation refuses with this Exception. Like the acquisition precondition, this is a state-machine invariant — retrying the identical call cannot succeed; the lease's real status must be reconciled first.","triggerScenarios":"Calling activateOnResource() on a freshly-created PENDING lease without calling acquireOnResource() first; double activation from two workers; activating a lease object whose in-memory status predates another daemon's transition.","commonSituations":"Custom blueprints implementing their own activation step (the stock path is in DrydockWorkingCopyBlueprintImplementation, which calls activateOnResource after acquiring); retry wrappers that re-enter activation after a partial failure; manual state poking during debugging.","solutions":["Follow the canonical order: acquireOnResource() first, then activateOnResource() — see DrydockWorkingCopyBlueprintImplementation::executeCommand lease handling","Reload the lease before activating and assert status is STATUS_ACQUIRED; skip if another worker already advanced it","If the lease is still PENDING, queue it for normal processing (lease update worker) instead of forcing activation","If RELEASED/BROKEN, discard and create a new lease"],"exampleFix":"// before\n$lease->activateOnResource($resource);\n// Exception: Trying to activate a lease which has the wrong status: status must be \"acquired\", actually \"pending\".\n\n// after\n$lease = id(new DrydockLeaseQuery())\n  ->setViewer($viewer)\n  ->withPHIDs(array($lease->getPHID()))\n  ->executeOne();\nif ($lease->getStatus() !== DrydockLeaseStatus::STATUS_ACQUIRED) {\n  return; // not ready or already advanced\n}\n$lease->activateOnResource($resource);","handlingStrategy":"validation","validationCode":"// Assert ACQUIRED status before activating:\n$lease = id(new DrydockLeaseQuery())\n  ->setViewer($viewer)\n  ->withPHIDs(array($lease->getPHID()))\n  ->executeOne();\nif ($lease->getStatus() !== DrydockLeaseStatus::STATUS_ACQUIRED) {\n  // not acquired yet (or already advanced): reconcile instead of activating\n}\n$lease->activateOnResource($resource);","typeGuard":null,"tryCatchPattern":"catch (Exception $ex) { on 'must be \"acquired\"', reload the lease and branch: PENDING → run acquisition first; ACTIVE → nothing to do; RELEASED/BROKEN → create a new lease }","preventionTips":["Enforce the acquire → activate order in custom blueprint code; stock example is DrydockWorkingCopyBlueprintImplementation","Idempotency-check with a fresh load before every lifecycle call","Let the lease update worker drive transitions where possible instead of forcing them inline"],"tags":["drydock","phabricator","lease","state-machine","activation","lifecycle-order"],"backgroundTag":"invalid-state-transition","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}