{"record":{"id":"7055eb4e79ce2273","repo":"MHSanaei/3x-ui","slug":"s-s-w-cap-d-bytes","errorCode":null,"errorMessage":"%s %s: %w (cap %d bytes)","messagePattern":"(.+?) (.+?): %w \\(cap (.+?) bytes\\)","errorType":"exception","errorClass":"errRemoteResponseTooLarge","httpStatus":null,"severity":"error","filePath":"internal/web/runtime/remote.go","lineNumber":279,"sourceCode":"\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\n\tif err := json.Unmarshal(raw, &env); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode envelope: %w\", err)\n\t}\n\tif !env.Success {\n\t\treturn &env, &remoteAPIError{msg: env.Msg}\n\t}\n\treturn &env, nil\n}\n\nfunc (r *Remote) resolveRemoteID(ctx context.Context, tag string) (int, error) {\n\tif id, ok := r.cacheGetTag(tag); ok {\n\t\treturn id, nil\n\t}","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/runtime/remote.go#L261-L297","documentation":"Returned by Remote.do when the streamed body itself exceeds maxRemoteResponseBytes (readCappedBody surfaced errRemoteResponseTooLarge). This is the real guard for the case Content-Length lied, was absent (-1 under transparent decompression), or the body was zstd-compressed on the wire and inflated past the cap during reading.","triggerScenarios":"Node responds 200 with chunked encoding or Content-Encoding: zstd whose decompressed size exceeds the cap; declared length under the cap but actual bytes over it; a compromised or buggy node streaming unbounded data.","commonSituations":"Large inbound list served with transparent compression so the wire length is small but the decoded JSON is huge; proxy stripping Content-Length; node bug looping while serializing clients.","solutions":["Same triage as the declared-length variant: fetch the endpoint manually and measure the true decompressed size.","Reduce per-node payload (fewer inbounds/clients per node) if the data is legitimate.","Raising maxRemoteResponseBytes is a deliberate capacity decision — measure first, then change the constant in one place in remote.go.","Confirm no proxy is re-chunking responses and defeating the fast-fail path."],"exampleFix":"// before: 200 OK, chunked, 30MB decompressed inbound list\n// err: GET panel/api/inbounds/list: remote response too large (cap 8388608 bytes)\n\n// after: distribute inbounds across nodes, or measure and raise the cap\n// const maxRemoteResponseBytes = 64 << 20","handlingStrategy":"validation","validationCode":null,"typeGuard":"func isResponseTooLarge(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"remote response too large\")\n}","tryCatchPattern":"if err := fetchList(ctx); err != nil {\n    if isResponseTooLarge(err) {\n        return nil, fmt.Errorf(\"node %d payload exceeds cap: shard inbounds or raise maxRemoteResponseBytes\", nodeID)\n    }\n    return nil, err\n}","preventionTips":["Remember the cap applies to DECOMPRESSED size — compressed transfers can still exceed it.","Re-measure payload sizes when enabling zstd between master and node.","Change maxRemoteResponseBytes only after measuring; document the new budget."],"tags":["http","resource-limits","decompression","remote","payload-size"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}