{"record":{"id":"0592a9d762d942fd","repo":"phacility/phabricator","slug":"password-providers-can-t-be-linked","errorCode":null,"errorMessage":"Password providers can't be linked.","messagePattern":"Password providers can't be linked\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/auth/provider/PhabricatorPasswordAuthProvider.php","lineNumber":163,"sourceCode":"      ->setUser($viewer)\n      ->addHiddenInput('invite', true)\n      ->appendChild(\n        id(new AphrontFormTextControl())\n          ->setLabel(pht('Username'))\n          ->setName('username'));\n\n    $dialog = id(new AphrontDialogView())\n      ->setUser($viewer)\n      ->setTitle(pht('Register an Account'))\n      ->appendForm($form)\n      ->setSubmitURI('/auth/register/')\n      ->addSubmitButton(pht('Continue'));\n\n    return $dialog;\n  }\n\n  public function buildLinkForm($controller) {\n    throw new Exception(pht(\"Password providers can't be linked.\"));\n  }\n\n  private function renderPasswordLoginForm(\n    AphrontRequest $request,\n    $require_captcha = false,\n    $captcha_valid = false) {\n\n    $viewer = $request->getUser();\n\n    $dialog = id(new AphrontDialogView())\n      ->setSubmitURI($this->getLoginURI())\n      ->setUser($viewer)\n      ->setTitle(pht('Log In'))\n      ->addSubmitButton(pht('Log In'));\n\n    if ($this->shouldAllowRegistration()) {\n      $dialog->addCancelButton(\n        '/auth/register/',","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/auth/provider/PhabricatorPasswordAuthProvider.php#L145-L181","documentation":"PhabricatorPasswordAuthProvider::buildLinkForm() throws unconditionally. The built-in password provider is a primary credential store, not an external identity source, so 'linking' it to an already logged-in account is a meaningless operation that Phabricator refuses rather than rendering a form for.","triggerScenarios":"Calling buildLinkForm() on a PhabricatorPasswordAuthProvider instance: custom code that iterates all enabled providers and renders link forms for each, or a user manually navigating to the account-linking URI (/auth/link/...) for the password provider.","commonSituations":"Extensions or custom controllers that assume every auth provider supports account linking; users hand-editing auth URLs; tooling that enumerates providers and calls every build*Form method.","solutions":["Skip password providers before building link forms (instanceof PhabricatorPasswordAuthProvider check or provider key comparison)","In custom UIs, only offer 'link account' for providers that actually implement an external handshake (OAuth/LDAP providers)","If you reached this via a URL, stop hand-editing /auth/link/ URIs and use the account settings UI"],"exampleFix":"// before\nforeach ($providers as $provider) {\n  $forms[] = $provider->buildLinkForm($controller); // fatal for password provider\n}\n\n// after\nforeach ($providers as $provider) {\n  if ($provider instanceof PhabricatorPasswordAuthProvider) {\n    continue;\n  }\n  $forms[] = $provider->buildLinkForm($controller);\n}","handlingStrategy":"type-guard","validationCode":"// Before rendering link forms across all enabled providers:\nforeach ($providers as $provider) {\n  if ($provider instanceof PhabricatorPasswordAuthProvider) {\n    continue; // primary credential provider: linking not applicable\n  }\n  $forms[] = $provider->buildLinkForm($controller);\n}","typeGuard":"function providerSupportsLinking(PhabricatorAuthProvider $provider) {\n  return !($provider instanceof PhabricatorPasswordAuthProvider);\n}","tryCatchPattern":"try {\n  $form = $provider->buildLinkForm($controller);\n} catch (Exception $ex) {\n  // Password provider hit by mistake: skip instead of fatal.\n  return null;\n}","preventionTips":["Never assume every enabled auth provider supports account linking - check the provider class first","Use provider-type checks (instanceof / getProviderKey()) before calling buildLinkForm()","Drive users through the standard account-settings UI rather than hand-built /auth/link/ URLs"],"tags":["phabricator","authentication","password-provider","account-linking","unsupported-operation"],"backgroundTag":"auth-account-linking-unsupported","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}