XTLS/Xray-core · warning
ambiguous process match for connection from ::: to :
Error message
ambiguous process match for connection from ::: to :
What it means
On macOS, two or more processes had sockets matching the queried connection at the same best match level, so the winner is ambiguous. The function refuses to guess and returns this error instead of a possibly wrong PID. It is a correctness guard against mis-attributing a connection.
Source
Thrown at common/net/find_process_darwin.go:121
ambiguousBest = false
break
}
if matchLevel > bestLevel {
bestPID = pid
bestLevel = matchLevel
ambiguousBest = false
continue
}
if matchLevel == bestLevel {
ambiguousBest = true
}
}
if bestLevel == darwinSocketNoMatch {
return 0, "", "", errors.New("process not found for ", network, " connection from ", srcIP, ":", srcPort, " to ", destIP, ":", destPort)
}
if ambiguousBest {
return 0, "", "", errors.New("ambiguous process match for ", network, " connection from ", srcIP, ":", srcPort, " to ", destIP, ":", destPort)
}
absPath, err := darwinProcessPath(bestPID)
if err != nil {
return 0, "", "", errors.New("could not get process path for PID ", bestPID, ": ", err)
}
absPath = filepath.ToSlash(absPath)
return int(bestPID), filepath.Base(absPath), absPath, nil
}
func darwinProcessSocketMatchLevel(pid int32, network string, srcAddr netip.Addr, srcPort uint16, dstAddr netip.Addr, dstPort uint16, hasDstAddr bool) (darwinSocketMatchLevel, error) {
fds, err := darwinProcessFDs(pid)
if err != nil {
return darwinSocketNoMatch, err
}
bestLevel := darwinSocketNoMatchView on GitHub (pinned to 7d214f8b09)
Solutions
- Do not treat ambiguity as fatal: fall back to non-process routing rules
- If you control the target app, avoid passing listener FDs to forked children or set SO_REUSEPORT off
- Pass destIP/destPort to the call; when hasDstAddr is true the matcher can distinguish directions and often break the tie
- Report both candidate PIDs is not supported by the API, so design the caller to tolerate an unresolved PID
Defensive patterns
Strategy: fallback
Type guard
func isAmbiguousProcessMatch(err error) bool {
return err != nil && strings.Contains(err.Error(), "ambiguous process match")
} Try / catch
if err != nil && isAmbiguousProcessMatch(err) {
// two PIDs tied (forked/inherited socket); do not guess
return routeWithoutProcess(ctx)
} Prevention
- Pass destIP and destPort — destination-aware matching breaks many ties
- Avoid process rules for apps known to fork with inherited listeners
- Never assume the error implies a specific PID; the API refuses to choose by design
When it happens
Trigger: A forked process sharing an inherited socket with its parent (both FD tables reference the same socket), or two processes with identical local address:port bindings (SO_REUSEPORT, e.g. launchd workers). Both then tie at the same darwinSocketMatchLevel.
Common situations: Apps that fork workers holding inherited listeners (nginx-style prefork), macOS system daemons using SO_REUSEPORT, and containerized/multi-process Go apps that pass FDs to children.
Related errors
- failed to determine if address is local:
- invalid source IP address:
- invalid destination IP address:
- failed to list processes
- process not found for connection from ::: to :
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/ca685e2155187e20.
Report an issue: GitHub.