MHSanaei/3x-ui · error

outbound subscription response body exceeds size limit

Error message

outbound subscription response body exceeds size limit

What it means

errOutboundSubscriptionBodyTooLarge is returned by readBoundedOutboundSubscriptionBody in internal/web/service/outbound_subscription.go when a fetched outbound subscription document exceeds maxOutboundSubscriptionBytes (8 MiB). The 8 MiB cap is deliberately larger than the 2 MiB user-facing subscription cap because outbound subscriptions aggregate many upstream outbounds into one document; crossing it means the upstream feed is abnormally large or hostile.

Source

Thrown at internal/web/service/outbound_subscription.go:61

				if m, ok := ob.(map[string]any); ok {
					tag, _ = m["tag"].(string)
				}
				logger.Warningf("%s: dropping outbound %q rejected by xray-core: %v", label, tag, buildErr)
				dropped = append(dropped, fmt.Sprintf("%s: %v", tag, buildErr))
				continue
			}
		}
		kept = append(kept, ob)
	}
	return kept, dropped
}

// maxOutboundSubscriptionBytes caps a single outbound subscription response.
// It is larger than the 2 MiB user-facing subscription cap because an outbound
// subscription may aggregate many upstream outbounds into one document.
const maxOutboundSubscriptionBytes int64 = 8 << 20

var errOutboundSubscriptionBodyTooLarge = errors.New("outbound subscription response body exceeds size limit")

func readBoundedOutboundSubscriptionBody(r io.Reader) ([]byte, error) {
	body, err := io.ReadAll(io.LimitReader(r, maxOutboundSubscriptionBytes+1))
	if err != nil {
		return nil, err
	}
	if int64(len(body)) > maxOutboundSubscriptionBytes {
		return nil, fmt.Errorf("%w (limit: %d bytes)", errOutboundSubscriptionBodyTooLarge, maxOutboundSubscriptionBytes)
	}
	return body, nil
}

// OutboundSubscriptionService manages remote outbound subscriptions.
type OutboundSubscriptionService struct {
	settingService SettingService
}

// NewOutboundSubscriptionService returns a service for managing outbound subscriptions.

View on GitHub (pinned to ad32144c42)

Solutions

  1. Fetch the URL with curl and check the actual size and content; confirm it is a valid outbound subscription document
  2. Trim the subscription at the provider side or point at a filtered/sliced URL that fits under 8 MiB
  3. If the feed is legitimately larger, raise maxOutboundSubscriptionBytes and rebuild, then re-test the refresh job
  4. Remove the offending subscription entry if it was added by mistake

Example fix

// before
const maxOutboundSubscriptionBytes int64 = 8 << 20

// after
const maxOutboundSubscriptionBytes int64 = 16 << 20
Defensive patterns

Strategy: try-catch

Validate before calling

resp, err := http.Get(subURL)
if err == nil {
    if resp.ContentLength > maxOutboundSubscriptionBytes {
        resp.Body.Close()
        return errOutboundSubscriptionBodyTooLarge
    }
    defer resp.Body.Close()
}

Type guard

func isOutboundSubTooLarge(err error) bool {
    return errors.Is(err, errOutboundSubscriptionBodyTooLarge)
}

Try / catch

body, err := readBoundedOutboundSubscriptionBody(resp.Body)
if err != nil {
    if errors.Is(err, errOutboundSubscriptionBodyTooLarge) {
        // drop or quarantine this subscription; do not retry in a tight loop
    }
    return err
}

Prevention

When it happens

Trigger: The outbound-subscription refresh job (or manual fetch of an outbound subscription URL) downloads a body larger than 8 MiB — e.g. a provider URL that now returns thousands of outbounds, an accidentally pasted wrong URL serving a huge file, or a server that ignores range/size expectations.

Common situations: Subscribing to a mega-aggregator provider list; the subscription URL pointing at a binary/artifact file instead of a config; a compromised or misconfigured upstream serving endless data.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/cd017e983caa728a. Report an issue: GitHub.