phacility/phabricator · error · Exception
Password providers can't be linked.
Error message
Password providers can't be linked.
What it means
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.
Source
Thrown at src/applications/auth/provider/PhabricatorPasswordAuthProvider.php:163
->setUser($viewer)
->addHiddenInput('invite', true)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Username'))
->setName('username'));
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Register an Account'))
->appendForm($form)
->setSubmitURI('/auth/register/')
->addSubmitButton(pht('Continue'));
return $dialog;
}
public function buildLinkForm($controller) {
throw new Exception(pht("Password providers can't be linked."));
}
private function renderPasswordLoginForm(
AphrontRequest $request,
$require_captcha = false,
$captcha_valid = false) {
$viewer = $request->getUser();
$dialog = id(new AphrontDialogView())
->setSubmitURI($this->getLoginURI())
->setUser($viewer)
->setTitle(pht('Log In'))
->addSubmitButton(pht('Log In'));
if ($this->shouldAllowRegistration()) {
$dialog->addCancelButton(
'/auth/register/',View on GitHub (pinned to 5720a38cfe)
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
Example fix
// before
foreach ($providers as $provider) {
$forms[] = $provider->buildLinkForm($controller); // fatal for password provider
}
// after
foreach ($providers as $provider) {
if ($provider instanceof PhabricatorPasswordAuthProvider) {
continue;
}
$forms[] = $provider->buildLinkForm($controller);
} Defensive patterns
Strategy: type-guard
Validate before calling
// Before rendering link forms across all enabled providers:
foreach ($providers as $provider) {
if ($provider instanceof PhabricatorPasswordAuthProvider) {
continue; // primary credential provider: linking not applicable
}
$forms[] = $provider->buildLinkForm($controller);
} Type guard
function providerSupportsLinking(PhabricatorAuthProvider $provider) {
return !($provider instanceof PhabricatorPasswordAuthProvider);
} Try / catch
try {
$form = $provider->buildLinkForm($controller);
} catch (Exception $ex) {
// Password provider hit by mistake: skip instead of fatal.
return null;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Expected '%s' in request!
- Unable to load your OAuth1 token secret from storage. It may
- ERR-INVALID-AUTH
- Subversion does not support searching for ancestors of a par
- Mercurial does not currently support searching for ancestors
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/0592a9d762d942fd.
Report an issue: GitHub.