phacility/phabricator · error · Exception

Request failed with errors: %s.

Error message

Request failed with errors: %s.

What it means

The Mailgun API answered with valid JSON, but the decoded object has no 'id' key - Mailgun only includes 'id' when a message is queued. The adapter therefore treats the response as a delivery failure and rethrows Mailgun's own 'message' field as the exception text. Note the code reads $response['message'] unconditionally, so a response with neither 'id' nor 'message' also produces an undefined-index error on top.

Source

Thrown at src/applications/metamta/adapter/PhabricatorMailMailgunAdapter.php:129

        $attachment->getData(),
        $attachment->getFilename(),
        $attachment->getMimeType());
    }

    list($body) = $future->resolvex();

    $response = null;
    try {
      $response = phutil_json_decode($body);
    } catch (PhutilJSONParserException $ex) {
      throw new PhutilProxyException(
        pht('Failed to JSON decode response.'),
        $ex);
    }

    if (!idx($response, 'id')) {
      $message = $response['message'];
      throw new Exception(
        pht(
          'Request failed with errors: %s.',
          $message));
    }
  }

}

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Read the exception text - it is Mailgun's own error message and names the problem ('Domain not found: ...', 'Sandbox subdomains...', 'unauthorized', 401/403 messages).
  2. Make sure the mailer's 'domain' and 'api-key' options belong to the same Mailgun account and the domain is verified (Mailgun dashboard, Sending > Domains).
  3. Send from an address on the verified domain (or from the sandbox postmaster address when deliberately testing).
  4. Check account status and quotas in the Mailgun dashboard if the message indicates limits or suspension.

Example fix

// before: from-address on a domain Mailgun has not verified
$mail->setFrom('no-reply@unverified-domain.net');

// after: from-address on the verified sending domain
$mail->setFrom('no-reply@mg.example.com');
Defensive patterns

Strategy: try-catch

Try / catch

try {
  $adapter->sendMessage($message);
} catch (Exception $ex) {
  $text = $ex->getMessage();
  // The text is Mailgun's own 'message' field: classify it.
  if (preg_match('/Domain not found|Sandbox subdomains|unauthorized/i', $text)) {
    // permanent: stop retrying, alert the admin to fix domain/api-key
    $message->setStatus(PhabricatorMailOutboundStatus::STATUS_FAIL);
  } else {
    throw $ex; // let the worker retry
  }
}

Prevention

When it happens

Trigger: sendMessage() through the mailgun adapter with: an API key belonging to a different account than the configured 'domain'; a 'from' address on a domain Mailgun has not verified for that account ('Sandbox subdomains are for test purposes only'); malformed or rejected recipients; suspended account/quota exhaustion - any JSON response lacking 'id'.

Common situations: Sandbox/test domain left in cluster.mailers after go-live while production mail uses a real 'from' domain; API key rotated in Mailgun but not in Phabricator; sender domain not (re)verified after DNS changes.

Related errors


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