XTLS/Xray-core · error
Dispatcher: Invalid destination.
Error message
Dispatcher: Invalid destination.
What it means
validatePaddingSchedule rejects a send range that does not fit inside the turn's overall length bounds or is internally unordered. The checks are sendMinLength >= minLength, sendMaxLength >= sendMinLength, and sendMaxLength <= maxLength, where minLength/maxLength come from paddingTurnBounds. The send range picks what this side actually transmits, so it must be a sub-range of the negotiated turn bounds.
Source
Thrown at app/dispatcher/default.go:326
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(ob.Target.Address) {
isFakeIP = true
}
if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
}
d.routedDispatch(ctx, outbound, destination)
}()
}
return inbound, nil
}
// DispatchLink implements routing.Dispatcher.
func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.Destination, outbound *transport.Link) error {
if !destination.IsValid() {
return errors.New("Dispatcher: Invalid destination.")
}
outbounds := session.OutboundsFromContext(ctx)
if len(outbounds) == 0 {
outbounds = []*session.Outbound{{}}
ctx = session.ContextWithOutbounds(ctx, outbounds)
}
ob := outbounds[len(outbounds)-1]
ob.OriginalTarget = destination
ob.Target = destination
content := session.ContentFromContext(ctx)
if content == nil {
content = new(session.Content)
ctx = session.ContextWithContent(ctx, content)
}
outbound = WrapLink(ctx, d.policy, d.stats, outbound)
sniffingRequest := content.SniffingRequest
if !sniffingRequest.Enabled {
d.routedDispatch(ctx, outbound, destination)View on GitHub (pinned to 7d214f8b09)
Solutions
- Clamp the send range to [turn.minLength, turn.maxLength]: sendMinLength >= minLength and sendMaxLength <= maxLength
- Ensure sendMinLength <= sendMaxLength
- If a wider send range is actually needed, raise minLength/maxLength of the turn itself first
Example fix
// before
paddingTurn{
minLength: 200, maxLength: 1000,
sendMinLength: 100, sendMaxLength: 2000,
}
// after
paddingTurn{
minLength: 200, maxLength: 1000,
sendMinLength: 200, sendMaxLength: 1000,
} Defensive patterns
Strategy: validation
Validate before calling
func sendRangeFits(t paddingTurn) bool {
if t.sendMinLength == 0 && t.sendMaxLength == 0 {
return true
}
return t.sendMinLength >= t.minLength &&
t.sendMaxLength >= t.sendMinLength &&
t.sendMaxLength <= t.maxLength
} Prevention
- Derive send range from the bounds in code (sendMin = minLength + jitter) instead of typing two independent ranges
- Validate before connecting, not on failure
- Watch for maxLength edits that forget to re-clamp the send range
When it happens
Trigger: A turn with sendMinLength/sendMaxLength set where sendMinLength < turn.minLength, sendMaxLength > turn.maxLength, or sendMaxLength < sendMinLength. Fails during validatePaddingSchedule before any bytes are written.
Common situations: Setting sendMaxLength larger than maxLength expecting the send range to widen the turn (it cannot); swapping min/max fields by hand; editing only one end of the range during tuning; stale maxLength after lowering the turn bound.
Related errors
- not a Service.
- FakeDNSEngine is not initialized, but such a sniffer is used
- Failed to convert address to Net IP.
- unexpected client IP length
- failed to create expected ip matcher
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/22dc7c4fdc926ead.
Report an issue: GitHub.