phacility/phabricator · error · PhutilProxyException

Expected JSON response from Duo.

Error message

Expected JSON response from Duo.

What it means

PhabricatorDuoFuture::didReceiveResult() decodes every Duo API response body with phutil_json_decode(); if the body is not valid JSON, the PhutilJSONParserException is wrapped in PhutilProxyException with this message. A non-JSON body means the HTTPS request reached something that is not the Duo JSON API: an HTML error/proxy page or a truncated response.

Source

Thrown at src/applications/auth/future/PhabricatorDuoFuture.php:146

      }

      $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 Duo.'),
        $ex);
    }

    return $data;
  }

}

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. From the web host run: curl -sS https://api-XXXX.duosecurity.com/ping — the body must be JSON; if not, the network path is at fault.
  2. Verify the provider API hostname matches the Duo integration exactly.
  3. Exempt *.duosecurity.com from TLS-inspection/filtering proxies.
  4. If Duo is having an outage, retry once service is restored.
  5. Confirm the integration/secret keys are valid, since some auth failures return non-JSON error pages.
Defensive patterns

Strategy: try-catch

Validate before calling

// Preflight from the web host before relying on Duo:
// curl -fsS https://api-xxxx.duosecurity.com/ping  -> must print JSON
$ping = id(new PhabricatorDuoFuture())
  ->setIntegrationKey($ikey)
  ->setSecretKey($skey)
  ->setAPIHostname($hostname)
  ->setMethod('ping')
  ->resolve(); // throws early and clearly if the path returns non-JSON

Try / catch

try {
  $result = $future->resolve();
} catch (PhutilProxyException $ex) {
  if ($ex->getPrevious() instanceof PhutilJSONParserException) {
    // the body was not JSON: routing/proxy/outage problem,
    // log raw context and report 'Duo API unreachable', then retry later
  }
  throw $ex;
}

Prevention

When it happens

Trigger: Resolving any PhabricatorDuoFuture ('enroll', 'enroll_status', 'preauth', 'ping') where the body is HTML or garbage: TLS interception proxy or captive portal returning HTML, wrong hostname resolving to a gateway page, Duo serving an HTML 5xx page during an outage, or a response truncated mid-transfer.

Common situations: Corporate networks with TLS inspection on *.duosecurity.com; DNS misdirection or hostname typo; transient Duo incidents; web host behind a filtering egress proxy.

Related errors


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