phacility/phabricator · error · PhutilProxyException

Expected JSON response from Twilio.

Error message

Expected JSON response from Twilio.

What it means

PhabricatorTwilioFuture wraps the Twilio REST API and expects JSON back: it first rethrows the HTTP status if isError(), then decodes the body with phutil_json_decode(). The status was fine but the body was not JSON, so it throws PhutilProxyException chaining the parser error. Something on the path - an authenticating proxy, TLS interception, a captive portal, or a misrouted host - returned HTML or plain text instead of a Twilio API payload.

Source

Thrown at src/applications/metamta/future/PhabricatorTwilioFuture.php:92

      }

      $this->future = $future;
    }

    return $this->future;
  }

  protected function didReceiveResult($result) {
    list($status, $body, $headers) = $result;

    if ($status->isError()) {
      throw $status;
    }

    try {
      $data = phutil_json_decode($body);
    } catch (PhutilJSONParserException $ex) {
      throw new PhutilProxyException(
        pht('Expected JSON response from Twilio.'),
        $ex);
    }

    return $data;
  }

}

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Inspect the chained PhutilJSONParserException - it contains the actual response body; identify whether it is a proxy/captive-portal page or an empty body.
  2. From the host running the daemons, verify you reach Twilio and get JSON: curl -sS https://api.twilio.com/2010-04-01/Accounts.json -u <SID>:<TOKEN>.
  3. If a proxy or TLS inspection is mandatory, exempt api.twilio.com from interception.
  4. Confirm the Twilio account SID / auth token in the mailer options so Twilio itself answers rather than an intermediate.
Defensive patterns

Strategy: retry

Try / catch

try {
  $future = id(new PhabricatorTwilioFuture($sid, $token))->setMethod(...)->resolve();
} catch (PhutilProxyException $ex) {
  if (preg_match('/Expected JSON response/', $ex->getMessage())) {
    // Body (in $ex->getPrevious()) is a proxy/error page: retry with backoff,
    // and page the operator if it repeats - the transport is broken.
    return retry_later();
  }
  throw $ex;
}

Prevention

When it happens

Trigger: SMS delivery through PhabricatorMailTwilioAdapter::sendMessage() (mail workers, 'bin/mail send-test' with an SMS message) or direct use of PhabricatorTwilioFuture while an intercepting proxy rewrites responses; wrong endpoint routing due to DNS or config; gateway error pages.

Common situations: Corporate egress proxies or SSL-inspection appliances MITMing api.twilio.com; on-prem installs behind captive portals; DNS pointing api.twilio.com at a wrong host. Twilio's own API errors are JSON (and usually surface via the status check), so a decode failure almost always means transport-layer interference.

Related errors


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