earendil-works/pi · error · Error

Unix listener path changed while checking for a stale socket

Error message

Unix listener path changed while checking for a stale socket: ${path}

What it means

Race detector in removeStaleSocket: the stale entry was renamed aside, but lstat of the preserved (.s-*) file shows it is no longer the same object (not a socket, or dev/ino differ from the original stat), and the original path was also recreated by someone else. The filesystem changed underneath the staleness check, so the removal is abandoned and the conflict reported instead of deleting an unknown file.

Solutions

  1. The socket path changed concurrently; retry and avoid races on the path.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at packages/server/src/transports/unix/listener.ts:338 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24). Data as JSON: /api/errors/37fa372e038c461d. Report an issue: GitHub.

Appendix: source

Thrown at packages/server/src/transports/unix/listener.ts:338

	if (!original.isSocket()) throw new Error(`Refusing to remove non-socket Unix listener path: ${path}`);
	if (await isSocketLive(path)) throw new Error(`Unix listener is already running: ${path}`);

	const preserved = join(dirname(path), `.s-${randomUUID().slice(0, 6)}`);
	try {
		await rename(path, preserved);
	} catch (error) {
		if (isErrorCode(error, "ENOENT")) return;
		throw error;
	}
	const current = await lstat(preserved);
	if (!current.isSocket() || current.dev !== original.dev || current.ino !== original.ino) {
		try {
			await lstat(path);
		} catch (error) {
			if (isErrorCode(error, "ENOENT")) await rename(preserved, path);
			else throw error;
		}
		throw new Error(`Unix listener path changed while checking for a stale socket: ${path}`);
	}
	await removePath(preserved);
}

async function removePath(path: string): Promise<void> {
	try {
		await unlink(path);
	} catch (error) {
		if (!isErrorCode(error, "ENOENT")) throw error;
	}
}

function isSocketLive(path: string): Promise<boolean> {
	return new Promise<boolean>((resolve, reject) => {
		const socket = createConnection(path);
		let settled = false;
		let timer: NodeJS.Timeout | undefined;
		const finish = (result: boolean, error?: Error): void => {

View on GitHub (pinned to 4af9d21d3b)