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
- 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.
- If a proxy must sit in front, make it pass WebSocket upgrades on that port rather than serving 200 itself.
- Re-run the probe after the change: the client port must answer plain GET with 501 for testClient() to return true.
- 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
- Point the client entry of notification.servers straight at the Aphlict client port; never route it through a web proxy or the web vhost.
- Health-check with curl: plain GET on the client port must answer 501; a 200 identifies an interceptor.
- Re-run testClient() after every load-balancer or reverse-proxy change.
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
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- Failed to read configuration file. %s
- Specify a user to notify with "--user".
- No user with username "%s" exists.
- Specify a message to send with "--message".
- Configuration file does not specify any client servers. This
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/6b5a73ac4ff7af2b.
Report an issue: GitHub.