XTLS/Xray-core · error · ErrNotLocal

the source address is not from local machine.

Error message

the source address is not from local machine.

What it means

ErrNotLocal is returned by net.IsLocal when the given IP does not match any address on the local machine's interfaces. The library uses it to verify that a dial/request source actually originates from this host (spoofing/loopback security check in transparent proxying).

Source

Thrown at common/net/net.go:22

import (
	"net"
	"sync/atomic"
	"time"

	"github.com/xtls/xray-core/common/errors"
)

// defines the maximum time an idle TCP session can survive in the tunnel, so
// it should be consistent across HTTP versions and with other transports.
const ConnIdleTimeout = 300 * time.Second

// consistent with quic-go
const QuicgoH3KeepAlivePeriod = 10 * time.Second

// consistent with chrome
const ChromeH2KeepAlivePeriod = 45 * time.Second

var ErrNotLocal = errors.New("the source address is not from local machine.")

type localIPCacheEntry struct {
	addrs      []net.Addr
	lastUpdate time.Time
}

var localIPCache = atomic.Pointer[localIPCacheEntry]{}

func IsLocal(ip net.IP) (bool, error) {
	var addrs []net.Addr
	if entry := localIPCache.Load(); entry == nil || time.Since(entry.lastUpdate) > time.Minute {
		var err error
		addrs, err = net.InterfaceAddrs()
		if err != nil {
			return false, err
		}
		localIPCache.Store(&localIPCacheEntry{
			addrs:      addrs,

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Check the address actually belongs to a local interface with `ip addr` / `ipconfig` and fix routing/NAT so the original source reaches the proxy unmodified
  2. If operating as a gateway for other machines, disable or adapt the local-source check rather than forcing traffic through it
  3. Verify you are not passing the remote destination IP instead of the source IP to IsLocal

Example fix

// before
if ok, _ := net.IsLocal(srcIP); !ok {
    return net.ErrNotLocal
}

// after (gateway mode: allow LAN clients)
if ok, _ := net.IsLocal(srcIP); !ok && !gatewayMode {
    return net.ErrNotLocal
}
Defensive patterns

Strategy: validation

Validate before calling

if ok, err := net.IsLocal(ip); err == nil && ok {
    // proceed: source is genuinely local
} else {
    return net.ErrNotLocal
}

Try / catch

if errors.Is(err, net.ErrNotLocal) { /* gateway mode: allow; strict mode: drop connection */ }

Prevention

When it happens

Trigger: Calling IsLocal(ip) with a remote peer IP, an IP from an interface that went down, or an IP whose interface list changed after the 1-minute localIPCache was populated is refreshed (it does refresh on mismatch). Typically triggered in transparent proxy handlers validating the original source of a connection.

Common situations: Misconfigured transparent proxy where the sniffed source is the LAN client rather than the local machine, containers/VMs whose interfaces change dynamically, or NAT scenarios where the source address was rewritten.

Related errors


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