XTLS/Xray-core · error

empty worker list

Error message

empty worker list

What it means

StaticMuxPicker (the portal's pool of bridge-backed mux workers) has zero registered workers when PickAvailable is called. Every tunneled connection must ride an existing worker; with an empty list there is no bridge connected at all, so picking fails. Typically returned up through the outbound dispatcher when traffic is routed to the portal tag but no bridge has established its tunnel.

Source

Thrown at app/reverse/portal.go:180

			activeWorkers = append(activeWorkers, w)
		} else {
			w.timer.SetTimeout(0)
		}
	}

	if len(activeWorkers) != len(p.workers) {
		p.workers = activeWorkers
	}

	return nil
}

func (p *StaticMuxPicker) PickAvailable() (*mux.ClientWorker, error) {
	p.access.Lock()
	defer p.access.Unlock()

	if len(p.workers) == 0 {
		return nil, errors.New("empty worker list")
	}

	var minIdx int = -1
	var minConn uint32 = 9999
	for i, w := range p.workers {
		if w.draining {
			continue
		}
		if w.IsFull() {
			continue
		}
		if w.client.ActiveConnections() < minConn {
			minConn = w.client.ActiveConnections()
			minIdx = i
		}
	}

	if minIdx == -1 {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Verify the bridge side is running and its outbound points at the portal's address/tag; check portal logs for accepted bridge connections.
  2. Ensure routing rules send traffic to the portal tag only when bridges exist, or add a fallbackTag/observability so the failure is visible.
  3. If workers keep disappearing, debug why bridge connections drop (see errors 91/92) — this error is usually the symptom, not the cause.
Defensive patterns

Strategy: fallback

Validate before calling

// Before routing traffic to the portal tag, confirm bridges are connected
if picker.Len() == 0 { // expose worker count from StaticMuxPicker
    return routeDirectOrReject(ctx, link) // no bridge tunnel exists yet
}

Type guard

func portalHasWorkers(picker reverse.WorkerCounter) bool { return picker.Len() > 0 }

Try / catch

if err := dispatch(ctx, link); err != nil {
    if strings.Contains(err.Error(), "empty worker list") {
        return routeViaFallbackTag(ctx, link) // or queue briefly until first bridge registers
    }
    return err
}

Prevention

When it happens

Trigger: Traffic dispatched to the portal's outbound tag while p.workers == 0 — i.e. before any bridge connected, or after all bridge workers were reaped (closed/drained and removed by the picker's cleanup that filters activeWorkers).

Common situations: Portal side up but bridge side down/offline (reverse client never connected, or lost network); bridge connects to the wrong portal address; startup ordering where routing sends traffic before the first bridge tunnel arrives; long outages where the last worker was cleaned up.

Related errors


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