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.OutboundSubscriptionView on GitHub (pinned to ad32144c42)
Solutions
- Fetch the URL with curl and check the actual size: curl -sL <url> | wc -c
- If legitimate, ask the provider for a filtered/smaller subscription or trim it upstream (the panel cap is a constant, not configurable)
- 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
- Use provider-side filtered subscription links for large node lists
- Treat a suddenly huge body as a wrong-URL smell (HTML page) and inspect it
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
- too many outbounds in one request (max %d)
- Failed to reserve test ports: %w
- stopped after 10 redirects
- http %d
- node port must be 1-65535
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/e930778df57cf8a5.
Report an issue: GitHub.