MHSanaei/3x-ui · error
%s %s: HTTP %d
Error message
%s %s: HTTP %d
What it means
Returned by Remote.do when the sub-node answers with a non-200 status and an EMPTY body, so there is nothing to quote. It carries only method, path and status code. Empty-body errors typically come from infrastructure between master and node rather than the node's own handlers, which almost always write a body.
Source
Thrown at internal/web/runtime/remote.go:266
}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("%s %s: %w", method, path, err)
}
defer resp.Body.Close()
r.recordCaps(resp.Header)
// Validate status before reading a success payload: a non-OK response's
// body is never used beyond a short diagnostic, so don't let a node force us
// to buffer a large body just to return an HTTP error.
if resp.StatusCode != http.StatusOK {
snippet, _ := io.ReadAll(io.LimitReader(resp.Body, errBodyDiagBytes))
if msg := bytes.TrimSpace(snippet); len(msg) > 0 {
// %q quotes/escapes the untrusted node body so control characters or
// newlines in it can't garble or inject into the error/log output.
return nil, fmt.Errorf("%s %s: HTTP %d: %q", method, path, resp.StatusCode, msg)
}
return nil, fmt.Errorf("%s %s: HTTP %d", method, path, resp.StatusCode)
}
// Fast-fail on an honestly-declared oversize body; the LimitReader below is
// the real guard since Content-Length is untrusted, may be absent, or is -1
// under transparent decompression.
if resp.ContentLength > maxRemoteResponseBytes {
return nil, fmt.Errorf("%s %s: %w (content-length %d, cap %d)", method, path, errRemoteResponseTooLarge, resp.ContentLength, maxRemoteResponseBytes)
}
raw, err := readCappedBody(resp.Body, maxRemoteResponseBytes)
if err != nil {
if errors.Is(err, errRemoteResponseTooLarge) {
return nil, fmt.Errorf("%s %s: %w (cap %d bytes)", method, path, err, maxRemoteResponseBytes)
}
return nil, fmt.Errorf("read body: %w", err)
}
var env envelopeView on GitHub (pinned to ad32144c42)
Solutions
- Bypass the proxy: hit the node's panel port directly from the master to see whether the node itself is healthy.
- Check what sits in front of the node port — proxy access logs will show why it emitted the bare status.
- Restart or fix the node service if the proxy reports the upstream is down.
- If the proxy must stay, ensure it forwards /panel/api/* (and the request bodies/headers) untouched to the node.
Example fix
// before: master -> nginx -> node(down) // err: POST panel/api/inbounds/update: HTTP 502 // after: start the node service (systemctl start x-ui) or point the master at the node's direct address // POST panel/api/inbounds/update -> 200
Defensive patterns
Strategy: retry
Type guard
func isBareStatusError(err error) bool {
_, hasSnippet := httpStatusOf(err) // see error 101
return hasSnippet && !strings.Contains(err.Error(), "\"") // no quoted body
} Try / catch
if err := r.do(ctx, method, path, body); err != nil {
if code, ok := httpStatusOf(err); ok && code >= 502 && code <= 504 {
return backoff.Retry(op, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 3))
}
return err
} Prevention
- Health-check the node directly (not through the proxy) before batch operations.
- Configure proxies to pass through /panel/api/* and log upstream failures with bodies.
- Treat bare 50x from a proxy as 'node down' — fix the upstream, do not retry into the same proxy blindly.
When it happens
Trigger: A reverse proxy or load balancer in front of the node returns 502/503/504 with no body (node process dead behind the proxy); the kernel/health-checker resets with 404; a TLS-terminating proxy returns 400 for a malformed request with no explanation; the node crashed mid-response.
Common situations: nginx/caddy in front of the node while the x-ui service is stopped; proxy misrouting /panel/api/* to a static site; proxy rejecting the request body or Content-Encoding before it reaches the node; connection upgrade path broken after a proxy config change.
Related errors
- %s %s: %w
- %s %s: HTTP %d: %q
- %s %s: %w (content-length %d, cap %d)
- %s %s: %w (cap %d bytes)
- decode envelope: %w
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/d36c7f4c1b04dc1a.
Report an issue: GitHub.