XTLS/Xray-core · error
unable to dispatch control connection
Error message
unable to dispatch control connection
What it means
NewPortalWorker fails to establish the control connection: it creates a pipe pair, synthesizes a context whose sole outbound targets the internal domain over UDP port 0, and calls client.Dispatch on the mux client; when Dispatch returns false the worker cannot be created. The control channel is what carries heartbeat/registration for the worker, so the tunnel setup aborts and surfaces (wrapped) as error 92's 'failed to create portal worker'.
Source
Thrown at app/reverse/portal.go:249
timer *signal.ActivityTimer
}
func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)}
uplinkReader, uplinkWriter := pipe.New(opt...)
downlinkReader, downlinkWriter := pipe.New(opt...)
ctx := context.Background()
outbounds := []*session.Outbound{{
Target: net.UDPDestination(net.DomainAddress(internalDomain), 0),
}}
ctx = session.ContextWithOutbounds(ctx, outbounds)
f := client.Dispatch(ctx, &transport.Link{
Reader: uplinkReader,
Writer: downlinkWriter,
})
if !f {
return nil, errors.New("unable to dispatch control connection")
}
terminate := func() {
client.Close()
}
w := &PortalWorker{
client: client,
reader: downlinkReader,
writer: uplinkWriter,
timer: signal.CancelAfterInactivity(ctx, terminate, 24*time.Hour), // // prevent leak
}
w.control = &task.Periodic{
Execute: w.heartbeat,
Interval: time.Second * 2,
}
w.control.Start()
return w, nil
}
View on GitHub (pinned to 7d214f8b09)
Solutions
- If occasional, rely on the bridge's automatic reconnect (monitor task) and treat as noise; persistent occurrence needs deeper checks.
- Match xray-core versions on both ends of the reverse tunnel.
- Check the carrier connection stability (NAT timeouts killing the tunnel at ~the same age, TLS issues) — a dying carrier makes Dispatch fail immediately.
Defensive patterns
Strategy: retry
Validate before calling
null // internal to NewPortalWorker; prevent by keeping the carrier link alive
Try / catch
if err := portal.HandleConnection(ctx, link); err != nil {
cause := deepestCause(err) // unwrap: 'failed to create portal worker' -> 'unable to dispatch control connection'
if strings.Contains(cause, "unable to dispatch control connection") {
return bridgeMonitor.ReconnectSoon() // transient race; retry via periodic monitor
}
return err
} Prevention
- Keep the portal-bridge carrier stable (keepalives, TLS, sane MTU)
- Match core versions across the tunnel
- Treat single occurrences as races; investigate only when persistent
When it happens
Trigger: mux.ClientWorker.Dispatch rejecting the control stream at construction time — worker already closed, its connection capacity exhausted, or internal mux state refusing new substreams before the worker finishes initializing.
Common situations: Race where the carrier connection to the portal dies between accept and control-dispatch; mux capacity edge conditions under load; version mismatches between bridge and portal cores. Generally transient, since bridges reconnect periodically.
Related errors
- failed to create portal worker
- failed to create mux client worker
- empty worker list
- no mux client worker available
- failed to process mux outbound traffic
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/36aa4e76c3545c8b.
Report an issue: GitHub.