phacility/phabricator · error · Exception
Duo API credential ("%s") has no secret key.
Error message
Duo API credential ("%s") has no secret key. What it means
The Passphrase credential referenced by the Duo provider exists, but $credential->getSecret() returns empty: the credential has no usable secret (never set, cleared, or destroyed). Duo's signed API requests require the secret key (S-key), so newDuoFuture() cannot construct PhabricatorDuoFuture and throws immediately.
Source
Thrown at src/applications/auth/factor/PhabricatorDuoAuthFactor.php:742
$omnipotent = PhabricatorUser::getOmnipotentUser();
$credential = id(new PassphraseCredentialQuery())
->setViewer($omnipotent)
->withPHIDs(array($credential_phid))
->needSecrets(true)
->executeOne();
if (!$credential) {
throw new Exception(
pht(
'Unable to load Duo API credential ("%s").',
$credential_phid));
}
$duo_key = $credential->getUsername();
$duo_secret = $credential->getSecret();
if (!$duo_secret) {
throw new Exception(
pht(
'Duo API credential ("%s") has no secret key.',
$credential_phid));
}
$duo_host = $provider->getAuthFactorProviderProperty(
self::PROP_HOSTNAME);
self::requireDuoAPIHostname($duo_host);
return id(new PhabricatorDuoFuture())
->setIntegrationKey($duo_key)
->setSecretKey($duo_secret)
->setAPIHostname($duo_host)
->setTimeout(10)
->setHTTPMethod('POST');
}
private function getDuoUsername(View on GitHub (pinned to 5720a38cfe)
Solutions
- Edit the Passphrase credential and set its secret to the Duo S-key.
- If the credential is destroyed, create a new credential with both the integration key (username) and secret key, then re-select it on the provider.
- Confirm the credential is a password-type credential as expected by the provider form.
- Test by issuing a Duo challenge after saving.
Defensive patterns
Strategy: validation
Validate before calling
// Run when saving the provider config.
$credential = id(new PassphraseCredentialQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($phid))
->needSecrets(true)
->executeOne();
$secret = $credential ? $credential->getSecret() : null;
if (!$secret || !strlen($secret->openEnvelope())) {
// reject the save: Duo requires integration key AND secret key
} Prevention
- Always set both integration key (username) and secret key when creating the Duo credential.
- Do not destroy credentials still referenced by providers; rotate instead.
- After any credential edit, run a Duo challenge to verify end-to-end.
When it happens
Trigger: Creating the provider's credential without a secret value, destroying the secret while keeping the credential row, or pointing the provider at a credential of the wrong shape. Any Duo API call for that provider then throws before any network traffic.
Common situations: Admin creates the credential with only the integration key and skips the secret; rotates keys and blanks the old secret; destroys the credential in Passphere/Passphrase UI but leaves the provider pointing at it.
Related errors
- Unable to load Duo API credential ("%s").
- This Duo enrollment attempt is invalid or has expired ("%s")
- Duo username pairing mode ("%s") is not supported.
- Duo enrollment mode ("%s") is not supported.
- Duo API hostname ("%s") is invalid, hostname must be "*.duos
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/e4a0e9732492e724.
Report an issue: GitHub.