{"record":{"id":"a7c381980c776abc","repo":"phacility/phabricator","slug":"object-owner-can-not-be-unlocked-the-unlocking-en","errorCode":null,"errorMessage":"Object owner can not be unlocked: the unlocking engine (\"%s\") for this object does not implement an owner unlocking mechanism.","messagePattern":"Object owner can not be unlocked: the unlocking engine \\(\"(.+?)\"\\) for this object does not implement an owner unlocking mechanism\\.","errorType":"console","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/system/engine/PhabricatorUnlockEngine.php","lineNumber":60,"sourceCode":"  public function newUnlockEditTransactions($object, $user) {\n    $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;\n\n    if (!$this->canApplyTransactionType($object, $type_edit)) {\n      throw new Exception(\n        pht(\n          'Object edit policy can not be unlocked because this object '.\n          'does not have a mutable edit policy.'));\n    }\n\n    return array(\n      $this->newTransaction($object)\n        ->setTransactionType($type_edit)\n        ->setNewValue($user->getPHID()),\n    );\n  }\n\n  public function newUnlockOwnerTransactions($object, $user) {\n    throw new Exception(\n      pht(\n        'Object owner can not be unlocked: the unlocking engine (\"%s\") for '.\n        'this object does not implement an owner unlocking mechanism.',\n        get_class($this)));\n  }\n\n  final protected function canApplyTransactionType($object, $type) {\n    $xaction_types = $object->getApplicationTransactionEditor()\n      ->getTransactionTypesForObject($object);\n\n    $xaction_types = array_fuse($xaction_types);\n\n    return isset($xaction_types[$type]);\n  }\n\n  final protected function newTransaction($object) {\n    return $object->getApplicationTransactionTemplate();\n  }","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/system/engine/PhabricatorUnlockEngine.php#L42-L78","documentation":"The base PhabricatorUnlockEngine::newUnlockOwnerTransactions() unconditionally throws: transferring ownership requires object-specific knowledge of how ownership is stored and changed, so only engines that override this method (returned by an object's newUnlockEngine()) support owner unlock. The default engine and most subclasses do not.","triggerScenarios":"Choosing the owner-unlock operation for an object whose engine is PhabricatorDefaultUnlockEngine or any subclass that does not override newUnlockOwnerTransactions().","commonSituations":"Attempting to reassign ownership of object types with no owner concept or whose application never implemented owner unlock; hitting the admin unlock dialog on an unsupported type.","solutions":["Use the view-policy or edit-policy unlock instead; both are implemented by the base engine","If this is your application, override newUnlockOwnerTransactions() in your PhabricatorUnlockEngine subclass and return the ownership-changing transactions","Hand-edit ownership through the object type's own admin workflow if one exists"],"exampleFix":"// before: relying on the base engine for owner unlock\n$engine = PhabricatorUnlockEngine::newUnlockEngineForObject($object);\n$xactions = $engine->newUnlockOwnerTransactions($object, $user);\n\n// after: implement the mechanism in your application's engine\nfinal class MyAppUnlockEngine extends PhabricatorUnlockEngine {\n  public function newUnlockOwnerTransactions($object, $user) {\n    return array(\n      $this->newTransaction($object)\n        ->setTransactionType(MyAppOwnerTransaction::TRANSACTIONTYPE)\n        ->setNewValue($user->getPHID()),\n    );\n  }\n}","handlingStrategy":"validation","validationCode":"// Owner unlock is supported only when the engine overrides the base method.\n$ref = new ReflectionMethod($engine, 'newUnlockOwnerTransactions');\nif ($ref->getDeclaringClass()->getName() === 'PhabricatorUnlockEngine') {\n  // unsupported: fall back to view/edit policy unlock\n}","typeGuard":null,"tryCatchPattern":"Catch Exception around newUnlockOwnerTransactions() and present the view/edit policy unlock options; the failure is structural (unimplemented mechanism), so retrying is never useful.","preventionTips":["Hide the owner-unlock action unless the engine overrides newUnlockOwnerTransactions()","Prefer policy unlocks, which the base engine always implements","In custom applications, implement owner unlock transactions when the type has ownership"],"tags":["unlock","ownership","transactions","admin"],"backgroundTag":"unsupported-operation-for-object-type","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}