{"record":{"id":"184f1dcdfa47ebf2","repo":"XTLS/Xray-core","slug":"brutal-184f1d","errorCode":null,"errorMessage":"brutal","messagePattern":"brutal","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"transport/internet/splithttp/hub.go","lineNumber":627,"sourceCode":"\ntype QListener struct {\n\thttp3.QUICListener\n\tquicParams *internet.QuicParams\n}\n\nfunc (l *QListener) Accept(ctx context.Context) (*quic.Conn, error) {\n\tconn, err := l.QUICListener.Accept(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tswitch l.quicParams.Congestion {\n\tcase \"reno\":\n\tcase \"\", \"bbr\":\n\t\tcongestion.UseBBR(conn, bbr.Profile(l.quicParams.BbrProfile))\n\tcase \"force-brutal\":\n\t\tcongestion.UseBrutal(conn, l.quicParams.BrutalUp)\n\tdefault:\n\t\tpanic(l.quicParams.Congestion)\n\t}\n\treturn conn, nil\n}\n","sourceCodeStart":609,"sourceCodeEnd":631,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/transport/internet/splithttp/hub.go#L609-L631","documentation":"Same unrecognized-congestion panic as the dialer, but on the inbound side: QListener.Accept panics with l.quicParams.Congestion the first time a QUIC client connects, because the per-conn congestion setup switch has the same default: panic branch. The listener itself starts fine — the crash is deferred until the first Accept, which makes it especially surprising.","triggerScenarios":"Starting a splithttp (HTTP/3 mode) hub with congestion set to anything other than \"reno\", \"\"/\"bbr\", \"force-brutal\"; the process panics inside Accept when the first inbound QUIC connection arrives.","commonSituations":"Server config edited to \"brutal\"/\"cubic\"/misspelled value; config validated at startup passes because the string is only interpreted lazily per accepted connection.","solutions":["Set congestion to \"reno\", \"bbr\"/\"\", or \"force-brutal\" (with brutalUp) in the inbound splithttp settings.","Check both sides: the dialer (outbound) and hub (inbound) each validate independently; fix both if the config is shared.","Add startup validation of the congestion field so misconfiguration fails before any client connects."],"exampleFix":"// before (inbound config)\n\"congestion\": \"brutal\"\n// after\n\"congestion\": \"bbr\"","handlingStrategy":"validation","validationCode":"validCongestion := map[string]bool{\"\": true, \"reno\": true, \"bbr\": true, \"force-brutal\": true}\nif !validCongestion[l.quicParams.Congestion] {\n    return nil, fmt.Errorf(\"hub: invalid congestion %q on inbound splithttp\", l.quicParams.Congestion)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate the inbound hub's congestion field at listener construction, since the panic hides until first Accept.","Fix both dialer and hub when correcting the config; they validate independently.","Smoke-test a real connection after every transport config change — startup success proves nothing here."],"tags":["quic","congestion-control","panic","config","listener","splithttp"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}