{"record":{"id":"f370f5d55f715914","repo":"different-ai/openwork","slug":"not-regular-file","errorCode":"NOT_REGULAR_FILE","errorMessage":"Expected a regular file","messagePattern":"Expected a regular file","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/server/src/jsonc.ts","lineNumber":60,"sourceCode":"}\n\n/**\n * Read a small diagnostics input without following symlinks or opening a FIFO\n * in blocking mode. The size is checked both before and while reading so a\n * file that grows after inspection cannot exceed the caller's memory budget.\n */\nexport async function readBoundedRegularTextFile(\n  path: string,\n  options: { maxBytes: number; signal?: AbortSignal },\n): Promise<string> {\n  if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 0) {\n    throw new RangeError(\"maxBytes must be a non-negative safe integer\");\n  }\n  throwIfAborted(options.signal);\n  const pathMetadata = await lstat(path);\n  throwIfAborted(options.signal);\n  if (!pathMetadata.isFile()) {\n    throw fileReadError(\"NOT_REGULAR_FILE\", \"Expected a regular file\");\n  }\n  if (pathMetadata.size > options.maxBytes) {\n    throw fileReadError(\"FILE_TOO_LARGE\", \"File exceeds the configured read limit\");\n  }\n\n  const nonBlockingFlags = process.platform === \"win32\"\n    ? 0\n    : constants.O_NONBLOCK | constants.O_NOFOLLOW;\n  const handle = await open(path, constants.O_RDONLY | nonBlockingFlags);\n  try {\n    const openedMetadata = await handle.stat();\n    throwIfAborted(options.signal);\n    if (!openedMetadata.isFile()) {\n      throw fileReadError(\"NOT_REGULAR_FILE\", \"Expected a regular file\");\n    }\n    if (openedMetadata.size > options.maxBytes) {\n      throw fileReadError(\"FILE_TOO_LARGE\", \"File exceeds the configured read limit\");\n    }","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/server/src/jsonc.ts#L42-L78","documentation":"readBoundedRegularTextFile refuses to read paths that lstat reports as anything other than a regular file, throwing an Error with code NOT_REGULAR_FILE. This blocks reading directories, FIFOs, sockets, and device nodes, preventing indefinite hangs on FIFOs and accidental device reads when loading small diagnostics/config files.","triggerScenarios":"Calling readJsoncFile or raw on a path that lstat shows is a directory, named pipe (FIFO), socket, device node, or similar non-regular file.","commonSituations":"Pointing a config path at a directory (e.g. ~/.config/openwork instead of a file inside it); a service creating a FIFO/socket where a JSONC file was expected; a Docker volume mounting a directory over a config file path.","solutions":["Point the path at an actual regular file, not a directory or socket.","Verify with fs.lstat(...).isFile() before calling.","Recreate the expected config file if something replaced it with a FIFO/socket.","If a symlink to a regular file is intended, copy the target to a regular file path instead."],"exampleFix":"// before\nconst cfg = await readJsoncFile(\"/etc/openwork\"); // directory -> NOT_REGULAR_FILE\n// after\nconst st = await fs.lstat(\"/etc/openwork/openwork.jsonc\");\nif (!st.isFile()) throw new Error(\"config path must be a regular file\");\nconst cfg = await readJsoncFile(\"/etc/openwork/openwork.jsonc\");","handlingStrategy":"validation","validationCode":"const st = await fs.lstat(path);\nif (!st.isFile()) throw new Error(`${path} is not a regular file`);","typeGuard":"null","tryCatchPattern":"try {\n  return await readJsoncFile(path);\n} catch (e) {\n  if ((e as NodeJS.ErrnoException).code === \"NOT_REGULAR_FILE\") return defaultConfig;\n  throw e;\n}","preventionTips":["Always lstat and isFile()-check user-supplied paths first.","Reject directories/sockets/FIFOs at config-load time with a clear message.","Watch for volume mounts that replace config file paths with directories.","Regenerate missing config files instead of pointing at placeholders."],"tags":["filesystem","path","jsonc","not-regular-file"],"backgroundTag":"not-a-regular-file","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}