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

  1. Create a new credential containing the private key and update the referencing blueprint/repository to use it.
  2. Plan rotation the other way around: add the new credential, switch references, verify operations, then destroy the old one (destroy is irreversible).
  3. 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

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


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/c41f9dc37c6fe238. Report an issue: GitHub.