{"record":{"id":"e7a263e086fa8735","repo":"can1357/oh-my-pi","slug":"ssh-remotepath-is-not-a-regular-file-fifo","errorCode":null,"errorMessage":"ssh://: ${remotePath} is not a regular file (FIFO, socket, or device); ssh:// reads UTF-8 text files only — use `bash` with a remote SSH command for special files","messagePattern":"ssh://: (.+?) is not a regular file \\(FIFO, socket, or device\\); ssh:// reads UTF-8 text files only — use `bash` with a remote SSH command for special files","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/ssh-protocol.ts","lineNumber":291,"sourceCode":"\t\t}\n\t\tconst target = await resolveTarget(url, context?.cwd);\n\t\tconst remotePath = remotePathFromUrl(url);\n\t\t// Classify before reading. A FIFO with no writer would block `head` until the\n\t\t// timeout, and a device (e.g. /dev/zero) would stream the whole probe, so a\n\t\t// special file must fail fast. Only a regular file is read; a directory lists.\n\t\t// `missing`/stat-failure falls through to the read so its original remote stderr\n\t\t// (e.g. \"No such file or directory\") still surfaces.\n\t\tlet kind: RemotePathKind | undefined;\n\t\ttry {\n\t\t\tkind = await statRemotePath(target, remotePath, { signal: context?.signal });\n\t\t} catch {\n\t\t\t// stat failed (host/connection issue) — fall through; the read gives a clearer error.\n\t\t}\n\t\tif (kind === \"directory\") {\n\t\t\treturn this.#resolveDirectory(target, remotePath, url, context?.signal, context?.skipDirectoryListing);\n\t\t}\n\t\tif (kind === \"other\") {\n\t\t\tthrow new Error(\n\t\t\t\t`ssh://: ${remotePath} is not a regular file (FIFO, socket, or device); ssh:// reads UTF-8 text files only — use \\`bash\\` with a remote SSH command for special files`,\n\t\t\t);\n\t\t}\n\t\tconst fileResult = await readRemoteFile(target, remotePath, {\n\t\t\tmaxBytes: SSH_TEXT_MAX_BYTES,\n\t\t\tsignal: context?.signal,\n\t\t});\n\t\tif (fileResult.truncated) {\n\t\t\tthrow new Error(\n\t\t\t\t`ssh://: ${remotePath} exceeds the 1 MiB limit; ssh:// supports text files up to 1 MiB — use an sshfs mount for larger files`,\n\t\t\t);\n\t\t}\n\t\tconst content = decodeUtf8Text(fileResult.bytes);\n\t\tif (content === null) {\n\t\t\tthrow new Error(\n\t\t\t\t`ssh://: ${remotePath} is a binary or non-UTF-8 file; ssh:// supports UTF-8 text only — use \\`bash\\` with a remote SSH command or an \\`sshfs\\` mount`,\n\t\t\t);\n\t\t}","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/ssh-protocol.ts#L273-L309","documentation":"Before reading, the handler stats the remote path; a path classified as `kind === \"other\"` (FIFO, socket, device) is rejected outright. Reading such a file would hang (FIFO with no writer) or stream unboundedly (e.g. /dev/zero), so it must fail fast. ssh:// only materializes regular UTF-8 text files.","triggerScenarios":"Resolving an ssh:// URL to a FIFO, Unix socket, or device node, e.g. `ssh://prod/dev/zero` or `ssh://prod/run/some.sock`; the remote `stat` classified the path as neither directory nor regular file.","commonSituations":"Pointing the read tool at device files or IPC sockets; probing named pipes used by services; accidentally targeting /proc or /sys pseudo-files.","solutions":["Run a remote command via the bash tool instead: `ssh prod 'cat /dev/...'` or read the device through a command that produces bounded text","Point the URL at a regular file on the remote host","If the special file proxies data, capture it to a regular file first (e.g. `ssh prod 'cmd > /tmp/out.txt'`) and read that"],"exampleFix":"// before\nresolve('ssh://prod/dev/zero')\n// after\nbash('ssh prod head -c 1024 /dev/zero | xxd') // special files via remote command","handlingStrategy":"validation","validationCode":"const kind = await statRemotePath(target, remotePath, { signal });\nif (kind === 'other') throw new Error(`${remotePath} is a FIFO/socket/device; use bash with a remote command`);","typeGuard":"function isRegularRemoteFile(k: RemotePathKind | undefined): boolean { return k === undefined || k === 'file'; }","tryCatchPattern":"try {\n  const res = await handler.resolve(url, ctx);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('is not a regular file')) {\n    // fall back to `bash` with a remote ssh command for the special file\n  } else throw e;\n}","preventionTips":["Don't point ssh:// at /dev/*, sockets, or FIFOs — reserve it for regular text files","Stat remote paths before reading when the path comes from dynamic discovery","Route special-file needs through remote commands (head/cat with limits) instead"],"tags":["ssh","file-type","special-file"],"backgroundTag":"not-a-regular-file","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}