{"record":{"id":"2ad57542bde63558","repo":"phacility/phabricator","slug":"expected-json-response-from-duo","errorCode":null,"errorMessage":"Expected JSON response from Duo.","messagePattern":"Expected JSON response from Duo\\.","errorType":"exception","errorClass":"PhutilProxyException","httpStatus":null,"severity":"error","filePath":"src/applications/auth/future/PhabricatorDuoFuture.php","lineNumber":146,"sourceCode":"      }\n\n      $this->future = $future;\n    }\n\n    return $this->future;\n  }\n\n  protected function didReceiveResult($result) {\n    list($status, $body, $headers) = $result;\n\n    if ($status->isError()) {\n      throw $status;\n    }\n\n    try {\n      $data = phutil_json_decode($body);\n    } catch (PhutilJSONParserException $ex) {\n      throw new PhutilProxyException(\n        pht('Expected JSON response from Duo.'),\n        $ex);\n    }\n\n    return $data;\n  }\n\n}\n","sourceCodeStart":128,"sourceCodeEnd":155,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/auth/future/PhabricatorDuoFuture.php#L128-L155","documentation":"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.","triggerScenarios":"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.","commonSituations":"Corporate networks with TLS inspection on *.duosecurity.com; DNS misdirection or hostname typo; transient Duo incidents; web host behind a filtering egress proxy.","solutions":["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.","Verify the provider API hostname matches the Duo integration exactly.","Exempt *.duosecurity.com from TLS-inspection/filtering proxies.","If Duo is having an outage, retry once service is restored.","Confirm the integration/secret keys are valid, since some auth failures return non-JSON error pages."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Preflight from the web host before relying on Duo:\n// curl -fsS https://api-xxxx.duosecurity.com/ping  -> must print JSON\n$ping = id(new PhabricatorDuoFuture())\n  ->setIntegrationKey($ikey)\n  ->setSecretKey($skey)\n  ->setAPIHostname($hostname)\n  ->setMethod('ping')\n  ->resolve(); // throws early and clearly if the path returns non-JSON","typeGuard":null,"tryCatchPattern":"try {\n  $result = $future->resolve();\n} catch (PhutilProxyException $ex) {\n  if ($ex->getPrevious() instanceof PhutilJSONParserException) {\n    // the body was not JSON: routing/proxy/outage problem,\n    // log raw context and report 'Duo API unreachable', then retry later\n  }\n  throw $ex;\n}","preventionTips":["Keep *.duosecurity.com out of TLS-inspection and filtering proxies.","Health-check /ping (expect JSON) before enrollment or debug sessions.","Treat any non-JSON Duo response as a routing/config fault, not a Duo logic result.","Verify integration/secret keys so auth failures do not surface as HTML error pages."],"tags":["duo","json","http-response","phabricator"],"backgroundTag":"invalid-json-response","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}