phacility/phabricator · error · Exception

Got HTTP 200, but expected HTTP 501 (WebSocket Upgrade)!

Error message

Got HTTP 200, but expected HTTP 501 (WebSocket Upgrade)!

What it means

PhabricatorNotificationServerRef::testClient() probes the notification server by issuing a plain HTTPSFuture GET to the client URI with a 2 second timeout. The Aphlict server correctly answers non-websocket HTTP with 501 'Not Implemented', so the catch block treats HTTPFutureHTTPResponseStatus with status 501 as success. If instead the request resolves without an exception (any 2xx, the message says 200), something else — usually a reverse proxy, load balancer, or the wrong port — is answering, and the method throws this generic Exception.

Source

Thrown at src/applications/notification/client/PhabricatorNotificationServerRef.php:211

      throw new Exception(
        pht('Unable to test client on an admin server!'));
    }

    $server_uri = $this->getURI();

    try {
      id(new HTTPSFuture($server_uri))
        ->setTimeout(2)
        ->resolvex();
    } catch (HTTPFutureHTTPResponseStatus $ex) {
      // This is what we expect when things are working correctly.
      if ($ex->getStatusCode() == 501) {
        return true;
      }
      throw $ex;
    }

    throw new Exception(
      pht('Got HTTP 200, but expected HTTP 501 (WebSocket Upgrade)!'));
  }

  public function loadServerStatus() {
    if (!$this->isAdminServer()) {
      throw new Exception(
        pht(
          'Unable to load server status: this is not an admin server!'));
    }

    $server_uri = $this->getURI('/status/');

    list($body) = $this->newFuture($server_uri)
      ->resolvex();

    return phutil_json_decode($body);
  }

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Fix notification.servers so the client server entry points directly at the Aphlict client port (default 22280/22261 with TLS), not through the proxy or at the web port.
  2. If a proxy must sit in front, make it pass WebSocket upgrades on that port rather than serving 200 itself.
  3. Re-run the probe after the change: the client port must answer plain GET with 501 for testClient() to return true.
  4. Verify with curl: curl -i http://host:client-port/ should yield HTTP/1.1 501; a 200 identifies whatever is intercepting.

Example fix

// before (notification.servers):
[{"type":"client","host":"phabricator.example","port":443,...}]
// port 443 is the web server -> GET returns 200 -> err: "Got HTTP 200, but expected HTTP 501"

// after:
[{"type":"client","host":"phabricator.example","port":22261,"protocol":"https",...}]
// Aphlict answers GET with 501 -> testClient() === true
Defensive patterns

Strategy: validation

Validate before calling

// Before calling testClient(), verify plain GET yields 501 from the exact client URI:
list($status) = id(new HTTPSFuture($ref->getURI()))
  ->setTimeout(2)
  ->resolve();
if (!($status instanceof HTTPFutureResponseStatus && $status->getStatusCode() === 501)) {
  // a proxy/web server is answering on the client port: fix notification.servers before testing
}

Try / catch

try {
  $ok = $ref->testClient();
} catch (Exception $ex) {
  if (preg_match('/expected HTTP 501/', $ex->getMessage())) {
    // wrong endpoint answering (proxy/LB/web port): correct the client entry in
    // notification.servers, exempt the Aphlict port from the proxy, then retry once
  }
  throw $ex;
}

Prevention

When it happens

Trigger: Calling $server_ref->testClient() (used by bin/notifications workflows and diagnostics) where getURI() points at a client port fronted by nginx/Apache/HAProxy that returns 200 for GET, or the URI was configured with an admin-port/web port instead of the Aphlict client port. resolvex() succeeds, execution falls through the try block, and the throw at line 211 fires.

Common situations: A proxy or health-check layer in front of every port; notification.servers config listing wrong host/port so the probe hits the web phabricator instance (which answers 200); TLS termination rewriting behavior; a firewall/redirect answering with 200; testing after adding an LB without excluding the Aphlict client port.

Understand the failure class

Related errors


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