XTLS/Xray-core · error

BrutalUp must be at least 65536 bytes per second

Error message

BrutalUp must be at least 65536 bytes per second

What it means

Validates the BrutalUp bandwidth under finalMask.quicParams: a positive value that parses to fewer than 65536 bytes/second is rejected. 65536 B/s is the floor Brutal CC needs for meaningful rate control. Zero (unset) is allowed and skips the check. The value is parsed by BrutalUp.Bps() which accepts bandwidth strings; a parse error there surfaces as a bare err instead.

Source

Thrown at infra/conf/transport_internet.go:239

			case "", string(bbr.ProfileConservative), string(bbr.ProfileStandard), string(bbr.ProfileAggressive):
				if profile == "" {
					profile = string(bbr.ProfileStandard)
				}
			default:
				return nil, errors.New("unknown bbr profile")
			}

			up, err := c.FinalMask.QuicParams.BrutalUp.Bps()
			if err != nil {
				return nil, err
			}
			down, err := c.FinalMask.QuicParams.BrutalDown.Bps()
			if err != nil {
				return nil, err
			}

			if up > 0 && up < 65536 {
				return nil, errors.New("BrutalUp must be at least 65536 bytes per second")
			}
			if down > 0 && down < 65536 {
				return nil, errors.New("BrutalDown must be at least 65536 bytes per second")
			}

			c.FinalMask.QuicParams.Congestion = strings.ToLower(c.FinalMask.QuicParams.Congestion)
			switch c.FinalMask.QuicParams.Congestion {
			case "", "brutal", "reno", "bbr":
			case "force-brutal":
				if up == 0 {
					return nil, errors.New("force-brutal requires up")
				}
			default:
				return nil, errors.New("unknown congestion control: ", c.FinalMask.QuicParams.Congestion, ", valid values: reno, bbr, brutal, force-brutal")
			}

			if (c.FinalMask.QuicParams.UdpHop.Interval.From != 0 && c.FinalMask.QuicParams.UdpHop.Interval.From < 5) || (c.FinalMask.QuicParams.UdpHop.Interval.To != 0 && c.FinalMask.QuicParams.UdpHop.Interval.To < 5) {
				return nil, errors.New("Interval must be at least 5")

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Raise brutalUp to at least 65536 bytes/second, e.g. "512 kBps" or higher.
  2. Or leave brutalUp unset (0) if you do not intend to pin upstream Brutal rate.
  3. Double-check bit-vs-byte units: 65536 B/s equals 524288 bits/s.

Example fix

// before
"quicParams": { "brutalUp": "100 kbps" }
// after
"quicParams": { "brutalUp": "512 kBps" }
Defensive patterns

Strategy: validation

Validate before calling

// parse bandwidth strings like "512 kBps" and enforce the floor
func brutalUpOK(raw any) bool {
    s, _ := raw.(string)
    if s == "" { return true } // unset
    bps, err := parseBandwidthToBytesPerSec(s)
    return err == nil && (bps == 0 || bps >= 65536)
}

Prevention

When it happens

Trigger: "quicParams": { "brutalUp": "32 kBps" } (or any positive value < 65536 B/s), e.g. "512 kbps" = 64000 B/s also fails.

Common situations: Confusing bits and bytes (65536 B/s = 524288 bps; "512 kbps" is below it); testing with tiny artificial bandwidth numbers; migrating values tuned for other proxies.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/dbf38be0e87de51a. Report an issue: GitHub.