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 := darwinSocketNoMatch

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Do not treat ambiguity as fatal: fall back to non-process routing rules
  2. If you control the target app, avoid passing listener FDs to forked children or set SO_REUSEPORT off
  3. Pass destIP/destPort to the call; when hasDstAddr is true the matcher can distinguish directions and often break the tie
  4. 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

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


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