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
- 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
- If operating as a gateway for other machines, disable or adapt the local-source check rather than forcing traffic through it
- 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
- errors.Is against the exported ErrNotLocal sentinel
- Ensure NAT preserves true source IPs to the proxy
- Decide gateway vs local mode explicitly in config
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
- No available name server could be created from
- invalid IP address
- asset path must stay in asset directory
- unable to get destination
- not a Service.
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/5bc2d9ab4f287c58.
Report an issue: GitHub.