{"record":{"id":"ffc93c4f2ecd1420","repo":"phacility/phabricator","slug":"unit-test-temporary","errorCode":null,"errorMessage":"Unit Test (Temporary)","messagePattern":"Unit Test \\(Temporary\\)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"info","filePath":"src/applications/metamta/adapter/PhabricatorMailTestAdapter.php","lineNumber":63,"sourceCode":"    return array();\n  }\n\n  public function supportsMessageIDHeader() {\n    return $this->supportsMessageID;\n  }\n\n  public function getGuts() {\n    return $this->guts;\n  }\n\n  public function sendMessage(PhabricatorMailExternalMessage $message) {\n    if ($this->failPermanently) {\n      throw new PhabricatorMetaMTAPermanentFailureException(\n        pht('Unit Test (Permanent)'));\n    }\n\n    if ($this->failTemporarily) {\n      throw new Exception(\n        pht('Unit Test (Temporary)'));\n    }\n\n    switch ($message->getMessageType()) {\n      case PhabricatorMailEmailMessage::MESSAGETYPE:\n        $guts = $this->newEmailGuts($message);\n        break;\n      case PhabricatorMailSMSMessage::MESSAGETYPE:\n        $guts = $this->newSMSGuts($message);\n        break;\n    }\n\n    $guts['did-send'] = true;\n    $this->guts = $guts;\n  }\n\n  public function getBody() {\n    return idx($this->guts, 'body');","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/metamta/adapter/PhabricatorMailTestAdapter.php#L45-L81","documentation":"The other failure-injection mode of PhabricatorMailTestAdapter: setFailTemporarily(true) makes sendMessage() throw a plain Exception with 'Unit Test (Temporary)'. Unlike the permanent variant, a plain exception from a mailer is treated by PhabricatorMetaMTAWorker as a transient failure - the message stays queued and the worker retries with backoff. It exists so tests can exercise retry behavior.","triggerScenarios":"Tests or scripts that construct the test adapter, call setFailTemporarily(true), and send a message; verifying that the worker re-queues PhabricatorMetaMTAWorker tasks and eventually fails the mail after exhausting retries.","commonSituations":"Writing Phabricator mail-retry tests; QA simulating flaky SMTP. If observed outside tests, the same rule applies: only explicit setFailTemporarily(true) enables it - config type 'test' accepts no options.","solutions":["Locate the caller of setFailTemporarily(true) - it is always programmatic, never config-driven.","If testing retries, watch the queue: each attempt schedules the worker again; after the retry limit the mail is marked failed.","Remove the flag when you want the test adapter to 'deliver' normally (it just records guts)."],"exampleFix":"// before (test)\n$mailer = id(new PhabricatorMailTestAdapter())->setFailTemporarily(true);\n\n// after (test)\n$mailer = id(new PhabricatorMailTestAdapter());","handlingStrategy":"validation","validationCode":"// In tests that do not target retry behavior, never arm the flag.\n$mailer = id(new PhabricatorMailTestAdapter());\nif ($testing_retries) {\n  $mailer->setFailTemporarily(true);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Scope setFailTemporarily() to tests that explicitly assert worker retry counts.","Remember the semantics: a plain Exception from sendMessage() means temporary failure and the worker retries with backoff."],"tags":["phabricator","metamta","testing","mail","failure-injection","retry"],"backgroundTag":"test-failure-injection","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}