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
- Remove the special file (`rm <path>`) and let the download create a fresh regular file.
- Download to a different filename that does not already exist as a FIFO/device.
- 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
- Never create FIFOs/devices in directories used as download targets.
- Check `ls -l` file type (p/s/b/c) before reusing a path as a destination.
- Prefer fresh, non-existent destination filenames for downloads.
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
- Refusing to download onto a non-regular file: ${absolutePath
- Refusing to download onto a file with ${stat.nlink} hard lin
- The managed-skills root is a symlink; refusing to operate ou
- Managed skill "${name}" SKILL.md is a symlink; refusing to o
- Managed skill "${name}" resolves through a symlink; refusing
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b1542ed1362ee0cc.
Report an issue: GitHub.