can1357/oh-my-pi · error · Error

ENXIO

ENXIO

Error message

Refusing to download onto a special file: ${absolutePath}

What it means

MCP resource downloads are written with O_NOFOLLOW|O_EXCL-style safety: the target must be a regular file, not a FIFO, device, socket, or other special inode. Opening a readerless FIFO with O_NONBLOCK fails with ENXIO, which this code re-reports explicitly as a refusal instead of the cryptic 'no such device or address'. It protects users from having a download clobber or block on a special file.

Source

Thrown at packages/coding-agent/src/cursor.ts:186

 * FIFO blocks until a reader arrives, so a `download_path` naming one would
 * hang the turn forever WITHOUT the non-regular check ever running — the open
 * itself never returns. Non-blocking turns that into `ENXIO` when no reader is
 * attached, and hands back a descriptor the `isFile()` check refuses when one
 * is. The flag has no effect on regular files, which is every legitimate
 * target.
 */
async function writeWithoutFollowingLinks(absolutePath: string, payload: string | Buffer): Promise<void> {
	await fs.promises.mkdir(path.dirname(absolutePath), { recursive: true });
	const handle = await fs.promises
		.open(
			absolutePath,
			fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK,
		)
		.catch((error: NodeJS.ErrnoException) => {
			// A readerless FIFO. Reported as the refusal it is, rather than the
			// bare "no such device or address" the errno spells out.
			if (error.code === "ENXIO") {
				throw new Error(`Refusing to download onto a special file: ${absolutePath}`);
			}
			throw error;
		});
	try {
		const stat = await handle.stat();
		if (!stat.isFile()) {
			throw new Error(`Refusing to download onto a non-regular file: ${absolutePath}`);
		}
		if (stat.nlink > 1) {
			throw new Error(
				`Refusing to download onto a file with ${stat.nlink} hard links, which would overwrite its other names: ${absolutePath}`,
			);
		}
		await handle.truncate(0);
		await handle.writeFile(payload);
	} finally {
		await handle.close();
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Remove the special file (`rm <path>`) and let the download create a fresh regular file.
  2. Download to a different filename that does not already exist as a FIFO/device.
  3. If the FIFO is in use by a reader, run the reader first, or don't route the download through the pipe.

Example fix

// before: destination is a FIFO
$ mkfifo /tmp/report.pdf  # download to /tmp/report.pdf -> ENXIO
// after
$ rm /tmp/report.pdf && retry the download to /tmp/report.pdf
Defensive patterns

Strategy: validation

Validate before calling

import { stat } from "node:fs/promises";
const s = await stat(downloadPath).catch(() => null);
if (s && !s.isFile()) throw new Error(`Refusing: ${downloadPath} is not a regular file`);

Type guard

function isRegularFile(s: { isFile(): boolean } | undefined): boolean {
  return !!s && s.isFile();
}

Try / catch

try {
  await downloadResource(res, downloadPath);
} catch (e) {
  if (String(e.message).startsWith("Refusing to download onto a special file")) {
    // pick another destination or remove the FIFO first
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the resource-download path (readMcpResource with downloadPath) where the destination path is a named pipe (mkfifo), a device node, socket, or other non-regular special file.

Common situations: Pointing a download at an existing FIFO created for inter-process piping; a leftover pipe file in a download directory; trying to 'write through' a device like /dev/stdout.

Related errors


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