can1357/oh-my-pi · error · Error

Port ${port} is in use, but the listening process could not

Error message

Port ${port} is in use, but the listening process could not be identified.

What it means

reclaimStatsPort() calls findPortHolder() to map the port to a owning process. When the port is reported in use but findPortHolder() returns null — no owning process could be identified from the OS tooling — this error is thrown rather than killing an unknown target. It is a deliberate safety stop: without a PID/image the code will not attempt termination.

Source

Thrown at packages/stats/src/port-conflict.ts:224

		} catch (error) {
			if (error instanceof Error && "code" in error && error.code === "ESRCH") return;
			throw new Error(`Failed to inspect ${holder.image} (PID ${holder.pid})`, { cause: error });
		}
	}

	try {
		process.kill(holder.pid, "SIGKILL");
	} catch (error) {
		if (error instanceof Error && "code" in error && error.code === "ESRCH") return;
		throw new Error(`Failed to kill ${holder.image} (PID ${holder.pid})`, { cause: error });
	}
	await Bun.sleep(PROCESS_EXIT_POLL_MS);
}

async function reclaimStatsPort(port: number): Promise<"retry"> {
	const holder = await findPortHolder(port);
	if (!holder) {
		throw new Error(`Port ${port} is in use, but the listening process could not be identified.`);
	}
	if (holder.pid === process.pid) {
		throw new Error(`Port ${port} is held by the current process (${holder.image}, PID ${holder.pid}).`);
	}

	const normalizedImage = holder.image
		.toLowerCase()
		.replace(/\.exe$/, "")
		.replace(/ \(deleted\)$/, "");
	const normalizedCommand = holder.commandLine.toLowerCase().replaceAll("\\", "/");
	const hasStatsIdentity =
		normalizedImage === "omp-stats" ||
		/(?:^|[/"'\s])omp-stats(?:\.exe)?(?:["'\s]|$)/.test(normalizedCommand) ||
		/\/packages\/stats\/src\/index\.ts(?:["'\s]|$)/.test(normalizedCommand) ||
		(normalizedImage === "omp" && /(?:^|\s)stats(?:\s|$)/.test(normalizedCommand)) ||
		/(?:^|\/)omp(?:\.exe)?["'\s]+stats(?:["'\s]|$)/.test(normalizedCommand);
	if (!STATS_RUNTIME_IMAGES[normalizedImage] || !hasStatsIdentity) {
		throw new Error(

View on GitHub (pinned to 9690622007)

Solutions

  1. Install or expose the process-listing tooling the environment needs (lsof/ss) so the holder can be identified
  2. Wait a moment and retry — the holder may have exited between checks
  3. Free the port manually (netstat -ano / lsof -i :<port>, then kill the PID) and restart the stats server
  4. Configure the stats server to use a different port

Example fix

// before
await prepareStatsPort(port); // throws when holder unidentifiable
// after
try {
  await prepareStatsPort(port);
} catch (err) {
  if (err.message.includes('could not be identified')) {
    await Bun.sleep(1000);
    await prepareStatsPort(port); // holder may have exited; or pick another port
  } else throw err;
}
Defensive patterns

Strategy: retry

Validate before calling

const busy = await isPortInUse(port);
const holder = busy ? await findPortHolder(port) : null;
if (busy && !holder) console.warn(`Port ${port} busy but owner unidentified — resolve manually before starting stats`);

Try / catch

try {
  await prepareStatsPort(port);
} catch (err) {
  if (err.message.includes('could not be identified')) {
    await Bun.sleep(1500);
    await prepareStatsPort(port); // holder may have exited; else pick another port
  } else throw err;
}

Prevention

When it happens

Trigger: Port is occupied (bind/listen fails) but findPortHolder() cannot resolve an owner — e.g. lsof/ss output unavailable or empty for that port, a foreign-namespace socket, or a race where the holder exited between the bind attempt and the lookup.

Common situations: Minimal containers or Windows environments lacking lsof/netstat equivalents the lookup relies on; a socket held by a kernel service; the port just released but still in TIME_WAIT while a probe still reports it busy.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/93ff5bdea7d69705. Report an issue: GitHub.