{"record":{"id":"d36c7f4c1b04dc1a","repo":"MHSanaei/3x-ui","slug":"s-s-http-d","errorCode":null,"errorMessage":"%s %s: HTTP %d","messagePattern":"(.+?) (.+?): HTTP (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/runtime/remote.go","lineNumber":266,"sourceCode":"\t}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s %s: %w\", method, path, err)\n\t}\n\tdefer resp.Body.Close()\n\tr.recordCaps(resp.Header)\n\n\t// Validate status before reading a success payload: a non-OK response's\n\t// body is never used beyond a short diagnostic, so don't let a node force us\n\t// to buffer a large body just to return an HTTP error.\n\tif resp.StatusCode != http.StatusOK {\n\t\tsnippet, _ := io.ReadAll(io.LimitReader(resp.Body, errBodyDiagBytes))\n\t\tif msg := bytes.TrimSpace(snippet); len(msg) > 0 {\n\t\t\t// %q quotes/escapes the untrusted node body so control characters or\n\t\t\t// newlines in it can't garble or inject into the error/log output.\n\t\t\treturn nil, fmt.Errorf(\"%s %s: HTTP %d: %q\", method, path, resp.StatusCode, msg)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"%s %s: HTTP %d\", method, path, resp.StatusCode)\n\t}\n\n\t// Fast-fail on an honestly-declared oversize body; the LimitReader below is\n\t// the real guard since Content-Length is untrusted, may be absent, or is -1\n\t// under transparent decompression.\n\tif resp.ContentLength > maxRemoteResponseBytes {\n\t\treturn nil, fmt.Errorf(\"%s %s: %w (content-length %d, cap %d)\", method, path, errRemoteResponseTooLarge, resp.ContentLength, maxRemoteResponseBytes)\n\t}\n\n\traw, err := readCappedBody(resp.Body, maxRemoteResponseBytes)\n\tif err != nil {\n\t\tif errors.Is(err, errRemoteResponseTooLarge) {\n\t\t\treturn nil, fmt.Errorf(\"%s %s: %w (cap %d bytes)\", method, path, err, maxRemoteResponseBytes)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"read body: %w\", err)\n\t}\n\n\tvar env envelope","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/runtime/remote.go#L248-L284","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: master -> nginx -> node(down)\n// err: POST panel/api/inbounds/update: HTTP 502\n\n// after: start the node service (systemctl start x-ui) or point the master at the node's direct address\n// POST panel/api/inbounds/update -> 200","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isBareStatusError(err error) bool {\n    _, hasSnippet := httpStatusOf(err) // see error 101\n    return hasSnippet && !strings.Contains(err.Error(), \"\\\"\") // no quoted body\n}","tryCatchPattern":"if err := r.do(ctx, method, path, body); err != nil {\n    if code, ok := httpStatusOf(err); ok && code >= 502 && code <= 504 {\n        return backoff.Retry(op, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 3))\n    }\n    return err\n}","preventionTips":["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."],"tags":["http","proxy","infrastructure","node-sync","remote"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}