{"record":{"id":"63be470db6dfb876","repo":"MHSanaei/3x-ui","slug":"read-body-w","errorCode":null,"errorMessage":"read body: %w","messagePattern":"read body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/runtime/remote.go","lineNumber":281,"sourceCode":"\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}\n\tif err := r.refreshRemoteIDs(ctx); err != nil {\n\t\treturn 0, err","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/runtime/remote.go#L263-L299","documentation":"Returned by Remote.do when reading the response body fails for a reason OTHER than the size cap — i.e. a transport-level failure mid-body. The status was 200 and headers were fine, but the stream broke before completion. The wrapped error is the raw io/network error from the body reader.","triggerScenarios":"Connection reset by the node or an intermediate proxy while streaming a large response; node process killed mid-response; TLS renegotiation failure; idle-timeout on a proxy cutting long transfers; context cancelled during the read.","commonSituations":"Flaky network path between master and node; proxy idle/response timeout shorter than the time to stream a big inbound list; node OOM-killed during serialization of a large payload.","solutions":["Retry the operation — mid-body failures are usually transient; the runtime marks the node dirty and ReconcileNode will converge state anyway.","If it recurs on the same node, check the node's logs for a crash (OOM, panic) at that timestamp.","Raise proxy read timeouts between master and node if large payloads consistently get cut.","Inspect the wrapped cause: 'context deadline exceeded' means the RPC timeout is too small for the payload, not a network fault."],"exampleFix":"// before: 10s RPC timeout, 20MB inbound list over slow link\n// err: read body: context deadline exceeded\n\n// after: raise the per-call timeout budget in the caller (context.WithTimeout) or reduce payload size\nctx, cancel := context.WithTimeout(ctx, 60*time.Second)","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isMidBodyFailure(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"read body:\") && !isResponseTooLarge(err)\n}","tryCatchPattern":"err := withRetry(3, func() error {\n    e, err := r.do(ctx, method, path, body)\n    env = e\n    return err\n}) // only retry when isMidBodyFailure(err)","preventionTips":["Set proxy read/idle timeouts between master and node above the largest expected transfer time.","Watch node memory: OOM-kills during serialization surface here as connection resets.","Size RPC context timeouts to the payload, not a fixed small constant."],"tags":["network","io","transient","remote","timeout"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}