XTLS/Xray-core · error
failed to create portal worker
Error message
failed to create portal worker
What it means
Portal-side failure one step after mux client creation: NewPortalWorker(muxClient) failed. The constructor (see error 95) dispatches the internal control connection over the mux client (to the internal domain destination) and returns 'unable to dispatch control connection' when client.Dispatch returns false; that inner error is wrapped here. Without the control connection the worker cannot register/heartbeat, so the whole portal connection is rejected.
Source
Thrown at app/reverse/portal.go:82
return p.ohm.RemoveHandler(context.Background(), p.tag)
}
func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) error {
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
if ob == nil {
return errors.New("outbound metadata not found").AtError()
}
if isDomain(ob.Target, p.domain) {
muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{})
if err != nil {
return errors.New("failed to create mux client worker").Base(err).AtWarning()
}
worker, err := NewPortalWorker(muxClient)
if err != nil {
return errors.New("failed to create portal worker").Base(err)
}
p.picker.AddWorker(worker)
if _, ok := link.Reader.(*pipe.Reader); !ok {
select {
case <-ctx.Done():
case <-muxClient.WaitClosed():
}
}
return nil
}
if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address {
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
}
View on GitHub (pinned to 7d214f8b09)
Solutions
- Treat as transient first: the bridge's periodic monitor reconnects every few seconds; check whether errors persist or self-heal.
- Upgrade both portal and bridge instances to matching xray-core versions so mux control semantics agree.
- If persistent, inspect carrier link health (TLS, keepalives, MTU) between the two sides — a dying carrier kills the control dispatch.
Defensive patterns
Strategy: retry
Validate before calling
null // nothing caller-side prevents a control-dispatch failure inside the worker ctor
Try / catch
if err := portal.HandleConnection(ctx, link); err != nil {
if strings.Contains(err.Error(), "failed to create portal worker") {
return scheduleBridgeReconnect(2 * time.Second) // monitor interval; treat as transient
}
return err
} Prevention
- Expect the bridge monitor to re-establish tunnels; do not fail hard on one occurrence
- Pin identical xray-core versions on portal and bridge
- Watch carrier lifetime patterns (e.g. dies at fixed age -> NAT timeout; enable keepalive)
When it happens
Trigger: The freshly created mux.ClientWorker refuses the control-connection dispatch — typically because the worker is already closed/full or its internal state rejected the new stream during tunnel setup.
Common situations: Bridge/portal version mismatch changing mux behavior, the carrier connection dropping microseconds after acceptance (worker closes before control connect), or resource limits where the worker immediately reports full. Usually transient and retried by the next bridge connection.
Related errors
- unable to dispatch control connection
- 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/4437c6b2298bf299.
Report an issue: GitHub.