{"record":{"id":"34617b025e72f3c8","repo":"MHSanaei/3x-ui","slug":"remote-response-exceeds-size-limit","errorCode":null,"errorMessage":"remote response exceeds size limit","messagePattern":"remote response exceeds size limit","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/runtime/remote.go","lineNumber":49,"sourceCode":"// overhead can outweigh the savings.\nconst zstdMinBodyBytes = 1024\n\n// maxRemoteResponseBytes caps a single node RPC's response body. It bounds the\n// wire/decompressed size of one response — the real guard against a broken or\n// hostile node streaming an unbounded body. It is NOT a process-wide memory\n// bound: concurrent RPCs and the decoded JSON can each exceed it, so\n// endpoint-specific caps and a concurrency budget remain follow-ups. Node\n// responses (traffic snapshots, client-IP lists, inbound options) are JSON and\n// stay well under it.\nconst maxRemoteResponseBytes = 64 << 20 // 64 MiB\n\n// errBodyDiagBytes bounds how much of a non-OK error body we read for a\n// diagnostic snippet (and to let small-error connections be reused) without\n// buffering a potentially huge or hostile error payload.\nconst errBodyDiagBytes = 8 << 10 // 8 KiB\n\n// errRemoteResponseTooLarge is returned when a node response exceeds the cap.\nvar errRemoteResponseTooLarge = errors.New(\"remote response exceeds size limit\")\n\n// readCappedBody reads all of r but rejects bodies larger than limit, returning\n// errRemoteResponseTooLarge. It reads at most limit+1 bytes so a body of exactly\n// limit is accepted and the first oversize byte is detected without buffering\n// more.\nfunc readCappedBody(r io.Reader, limit int64) ([]byte, error) {\n\traw, err := io.ReadAll(io.LimitReader(r, limit+1))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif int64(len(raw)) > limit {\n\t\treturn nil, errRemoteResponseTooLarge\n\t}\n\treturn raw, nil\n}\n\ntype envelope struct {\n\tSuccess bool            `json:\"success\"`","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/runtime/remote.go#L31-L67","documentation":"errRemoteResponseTooLarge is returned by readCappedBody in internal/web/runtime/remote.go when a sub-node's HTTP response body exceeds maxRemoteResponseBytes (64 MiB). The runtime reads at most limit+1 bytes; seeing one extra byte proves the body is oversize and it aborts instead of buffering a potentially huge or hostile payload. This protects the master panel's memory when talking to remote nodes.","triggerScenarios":"A master panel RPC to a sub-node (traffic stats sync, client-IP list, inbound options fetch) whose JSON payload exceeds 64 MiB — typically a node with tens of thousands of clients/inbounds, or a node returning an unexpectedly huge/looping document.","commonSituations":"Very large multi-node deployments aggregating traffic snapshots; a misbehaving or compromised node replaying an oversized body; node/panel version mismatch where a newer node emits a much larger response format than the master expects.","solutions":["Reduce the payload at the node: fewer clients/inbounds per node, or split reporting across nodes","If the deployment legitimately exceeds 64 MiB, raise maxRemoteResponseBytes in remote.go and rebuild (mind master memory: concurrent RPCs each buffer up to the cap)","Check the node's response for a bug/huge expansion (query the node endpoint directly and inspect Content-Length)","Verify master and node run compatible versions of the runtime protocol"],"exampleFix":"// before\nconst maxRemoteResponseBytes = 64 << 20 // 64 MiB\n\n// after (only if the deployment genuinely needs it)\nconst maxRemoteResponseBytes = 128 << 20 // 128 MiB","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isRemoteResponseTooLarge(err error) bool {\n    return errors.Is(err, errRemoteResponseTooLarge)\n}","tryCatchPattern":"data, err := fetchFromNode(node)\nif err != nil {\n    if errors.Is(err, errRemoteResponseTooLarge) {\n        // alert: node payload > 64 MiB; do not retry blindly — inspect the node\n    }\n    return err\n}","preventionTips":["Keep per-node client/inbound counts bounded so JSON snapshots stay well under 64 MiB","Monitor node response sizes before they approach the cap","Treat a sudden oversize response as a possible node fault, not normal load"],"tags":["network","size-limit","multi-node","memory-safety"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}