MHSanaei/3x-ui · warning · errOutboundSubscriptionBodyTooLarge

outbound subscription response body exceeds size limit (limi

Error message

outbound subscription response body exceeds size limit (limit: %d bytes)

What it means

readBoundedOutboundSubscriptionBody reads the subscription response through io.LimitReader(body, 8 MiB + 1) and rejects anything longer. The 8 MiB cap (4x the user-facing 2 MiB subscription cap) exists because outbound subscriptions aggregate many upstream outbounds; the +1 trick lets it distinguish 'at limit' from 'over limit' without buffering unbounded data. errOutboundSubscriptionBodyTooLarge is a sentinel, so errors.Is works on the wrapped error.

Source

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

		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.
func NewOutboundSubscriptionService() *OutboundSubscriptionService {
	return &OutboundSubscriptionService{}
}

// List returns all subscriptions (newest first).
func (s *OutboundSubscriptionService) List() ([]*model.OutboundSubscription, error) {
	db := database.GetDB()
	var subs []*model.OutboundSubscription

View on GitHub (pinned to ad32144c42)

Solutions

  1. Fetch the URL with curl and check the actual size: curl -sL <url> | wc -c
  2. If legitimate, ask the provider for a filtered/smaller subscription or trim it upstream (the panel cap is a constant, not configurable)
  3. If it is an HTML page, the URL is wrong — fix the subscription URL

Example fix

curl -sL https://provider.example/sub | wc -c   # > 8388608 means rejection is expected
# use a filtered link, e.g. provider dashboards usually offer ?limit= or per-group URLs
Defensive patterns

Strategy: validation

Validate before calling

resp, err := client.Get(sub.Url)
...
if resp.ContentLength > 8<<20 {
    return errors.New("subscription document exceeds 8 MiB before download")
}

Try / catch

body, err := readBoundedOutboundSubscriptionBody(resp.Body)
if err != nil && errors.Is(err, errOutboundSubscriptionBodyTooLarge) {
    // sentinel is wrapped: report 'document too large', suggest a filtered URL; do not retry blindly
}

Prevention

When it happens

Trigger: Refreshing an outbound subscription whose server returns a document larger than 8,388,608 bytes.

Common situations: A provider URL that is actually an HTML error page or a listing of thousands of nodes; a copy-pasted wrong URL returning a huge file; an upstream that inflated its node list.

Related errors


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