{"record":{"id":"c41f9dc37c6fe238","repo":"phacility/phabricator","slug":"attempting-to-use-a-credential-s-but-the-cred","errorCode":null,"errorMessage":"Attempting to use a credential (\"%s\") but the credential secret has been destroyed!","messagePattern":"Attempting to use a credential \\(\"(.+?)\"\\) but the credential secret has been destroyed!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/passphrase/keys/PassphraseSSHKey.php","lineNumber":25,"sourceCode":"  public static function loadFromPHID($phid, PhabricatorUser $viewer) {\n    $key = new PassphraseSSHKey();\n    return $key->loadAndValidateFromPHID(\n      $phid,\n      $viewer,\n      PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE);\n  }\n\n  public function getKeyfileEnvelope() {\n    $credential = $this->requireCredential();\n\n    $file_type = PassphraseSSHPrivateKeyFileCredentialType::CREDENTIAL_TYPE;\n    if ($credential->getCredentialType() != $file_type) {\n      // If the credential does not store a file, write the key text out to a\n      // temporary file so we can pass it to `ssh`.\n      if (!$this->keyFile) {\n        $secret = $credential->getSecret();\n        if (!$secret) {\n          throw new Exception(\n            pht(\n              'Attempting to use a credential (\"%s\") but the credential '.\n              'secret has been destroyed!',\n              $credential->getMonogram()));\n        }\n\n        $temporary_file = new TempFile('passphrase-ssh-key');\n        Filesystem::changePermissions($temporary_file, 0600);\n        Filesystem::writeFile($temporary_file, $secret->openEnvelope());\n\n        $this->keyFile = $temporary_file;\n      }\n\n      return new PhutilOpaqueEnvelope((string)$this->keyFile);\n    }\n\n    return $credential->getSecret();\n  }","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/passphrase/keys/PassphraseSSHKey.php#L7-L43","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"$credential = id(new PassphraseCredentialQuery())\n  ->setViewer($viewer)\n  ->withPHIDs(array($phid))\n  ->setLimit(1)\n  ->executeOne();\nif (!$credential || $credential->getIsDestroyed()) {\n  // rebind a live credential before starting SSH operations\n}","typeGuard":"function credential_is_usable($credential) {\n  return $credential !== null && !$credential->getIsDestroyed();\n}","tryCatchPattern":"try {\n  $envelope = $key->getKeyfileEnvelope();\n} catch (Exception $ex) {\n  if (preg_match('/secret has been destroyed/', $ex->getMessage())) {\n    // halt allocation and prompt to rebind a fresh credential\n  }\n}","preventionTips":["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."],"tags":["phabricator","passphrase","credentials","ssh","drydock","destroyed-secret"],"backgroundTag":"credential-secret-destroyed","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}