phacility/phabricator · error · Exception
Attempting to use a credential ("%s") but the credential sec
Error message
Attempting to use a credential ("%s") but the credential secret has been destroyed! What it means
Thrown by PassphraseSSHKey::getKeyfileEnvelope() when a non-file SSH key credential must be written to a temporary file for ssh, but $credential->getSecret() returns null because the credential's secret was destroyed. Destroying a credential wipes its encrypted secret while keeping the row (for auditing/history), so any later attempt to materialize the key file fails here with the monogram in the message.
Source
Thrown at src/applications/passphrase/keys/PassphraseSSHKey.php:25
public static function loadFromPHID($phid, PhabricatorUser $viewer) {
$key = new PassphraseSSHKey();
return $key->loadAndValidateFromPHID(
$phid,
$viewer,
PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE);
}
public function getKeyfileEnvelope() {
$credential = $this->requireCredential();
$file_type = PassphraseSSHPrivateKeyFileCredentialType::CREDENTIAL_TYPE;
if ($credential->getCredentialType() != $file_type) {
// If the credential does not store a file, write the key text out to a
// temporary file so we can pass it to `ssh`.
if (!$this->keyFile) {
$secret = $credential->getSecret();
if (!$secret) {
throw new Exception(
pht(
'Attempting to use a credential ("%s") but the credential '.
'secret has been destroyed!',
$credential->getMonogram()));
}
$temporary_file = new TempFile('passphrase-ssh-key');
Filesystem::changePermissions($temporary_file, 0600);
Filesystem::writeFile($temporary_file, $secret->openEnvelope());
$this->keyFile = $temporary_file;
}
return new PhutilOpaqueEnvelope((string)$this->keyFile);
}
return $credential->getSecret();
}View on GitHub (pinned to 5720a38cfe)
Solutions
- Create a new credential containing the private key and update the referencing blueprint/repository to use it.
- Plan rotation the other way around: add the new credential, switch references, verify operations, then destroy the old one (destroy is irreversible).
- Before launching SSH operations, check the credential's isDestroyed flag and fail fast with an actionable message.
Defensive patterns
Strategy: validation
Validate before calling
$credential = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withPHIDs(array($phid))
->setLimit(1)
->executeOne();
if (!$credential || $credential->getIsDestroyed()) {
// rebind a live credential before starting SSH operations
} Type guard
function credential_is_usable($credential) {
return $credential !== null && !$credential->getIsDestroyed();
} Try / catch
try {
$envelope = $key->getKeyfileEnvelope();
} catch (Exception $ex) {
if (preg_match('/secret has been destroyed/', $ex->getMessage())) {
// halt allocation and prompt to rebind a fresh credential
}
} Prevention
- Rotate credentials add-first: create the new one, switch references, verify, then destroy.
- Before destroying a credential, search blueprints, repositories, and build plans for its PHID.
- Pre-check getIsDestroyed() at operation start and fail with a clear message.
When it happens
Trigger: Running Drydock allocations or repository SSH operations (git/hg fetch) whose blueprint still references a credential that was destroyed via /K<id>/destroy; any call to getKeyfileEnvelope() on a destroyed file-less SSH key credential.
Common situations: Key rotation done by destroying the old credential first while active leases/blueprints still point at it; 'cleanup' of seemingly unused credentials without checking active operations or blueprints.
Related errors
- Credential "%s" must provide "%s", but provides "%s"!
- A passphrase was provided for this private key, but it does
- This private key requires a passphrase, but the wrong passph
- This private key requires a passphrase, but no passphrase wa
- This private key could not be opened with the provided passp
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/c41f9dc37c6fe238.
Report an issue: GitHub.