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
- Raise brutalUp to at least 65536 bytes/second, e.g. "512 kBps" or higher.
- Or leave brutalUp unset (0) if you do not intend to pin upstream Brutal rate.
- 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
- Think in bytes per second: the floor is 65536 B/s (512 kbps is 64000 B/s and FAILS).
- Keep bandwidth strings in the documented format (number + unit like 'MBps', 'kBps').
- Add a pre-deploy lint rule for brutalUp/brutalDown >= 65536.
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
- BrutalDown must be at least 65536 bytes per second
- force-brutal requires up
- unknown bbr profile
- unknown congestion control: + c.FinalMask.QuicParams.Conges
- Interval must be at least 5
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/dbf38be0e87de51a.
Report an issue: GitHub.