{"record":{"id":"6c2d9628e04bf19c","repo":"affaan-m/ECC","slug":"unsafe-state-store-path-dbpath-database-path-is-not-a","errorCode":null,"errorMessage":"Unsafe state-store path '${dbPath}': database path is not a regular file","messagePattern":"Unsafe state-store path '(.+?)': database path is not a regular file","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/state-store/index.js","lineNumber":106,"sourceCode":"        }\n      }\n      stats = fs.lstatSync(currentPath);\n      assertNotSymlink(currentPath, stats);\n    }\n\n    if (!stats.isDirectory() && !isAllowedPlatformSymlink(currentPath, stats)) {\n      throw stateStorePathError(currentPath, 'an intermediate component is not a directory');\n    }\n  }\n\n  return absolutePath;\n}\n\nfunction assertSafeDatabaseFile(dbPath) {\n  const stats = lstatIfPresent(dbPath);\n  assertNotSymlink(dbPath, stats);\n  if (stats && !stats.isFile()) {\n    throw stateStorePathError(dbPath, 'database path is not a regular file');\n  }\n  return stats;\n}\n\nfunction readDatabaseFile(dbPath) {\n  assertSafeDatabaseFile(dbPath);\n  const noFollow = fs.constants.O_NOFOLLOW || 0;\n  const fileDescriptor = fs.openSync(dbPath, fs.constants.O_RDONLY | noFollow);\n  try {\n    const stats = fs.fstatSync(fileDescriptor);\n    if (!stats.isFile()) {\n      throw stateStorePathError(dbPath, 'database path is not a regular file');\n    }\n    return fs.readFileSync(fileDescriptor);\n  } finally {\n    fs.closeSync(fileDescriptor);\n  }\n}","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/state-store/index.js#L88-L124","documentation":"assertSafeDatabaseFile checks the database path before the store reads or writes it: it lstats the path (tolerating ENOENT, since first-run means the file does not exist yet), rejects symlinks, and rejects any existing entry that is not a regular file. The library throws this error because openDatabase/writeDatabaseFileAtomic/readDatabaseFile must only read and atomically replace an actual SQLite file — operating on a directory, FIFO, socket or device at dbPath would corrupt state or leak data. Note the file is allowed to not exist; it must merely be a regular file if it does exist.","triggerScenarios":"createStateStore({ dbPath }) where dbPath already exists as a directory (e.g. someone pre-created state.db as a folder, or passed a directory path as dbPath), or as any non-regular file (FIFO, socket). Triggered from openDatabase, writeDatabaseFileAtomic (first saveToDisk), or readDatabaseFile — the check throws before any I/O on the path.","commonSituations":"Passing a directory instead of a file path to dbPath (e.g. createStateStore({ dbPath: someDir }) instead of path.join(someDir, 'state.db')), a mount point named state.db, a tmpfs/IPC socket placed at the db path, or tooling that created the path as a directory on first run.","solutions":["Check the path: ls -la <dbPath>; if it is a directory, remove it (rm -rf or mv aside) so the library can create the regular SQLite file there","Pass the full FILE path as dbPath — if you have a directory, append the filename: createStateStore({ dbPath: path.join(dir, 'state.db') }) or rely on the default ~/.claude/ecc/state.db","If the path is a socket/FIFO from another process, stop that process or relocate the store with a different dbPath","If a mount shadows the path, unmount or re-point the mount and retry"],"exampleFix":"// before: dbPath points at a directory that exists\ncreateStateStore({ dbPath: '/var/lib/ecc' });\n// after: point at a regular file inside that directory\nimport path from 'path';\ncreateStateStore({ dbPath: path.join('/var/lib/ecc', 'state.db') });","handlingStrategy":"validation","validationCode":"import fs from 'fs';\n\nexport function assertDbPathUsable(dbPath) {\n  const st = fs.lstatSync(dbPath, { throwIfNoEntry: false });\n  if (st && st.isSymbolicLink()) {\n    throw new Error(`dbPath '${dbPath}' is a symlink; refusing to use it`);\n  }\n  if (st && !st.isFile()) {\n    throw new Error(`dbPath '${dbPath}' exists but is not a regular file (${st.isDirectory() ? 'directory' : 'special file'})`);\n  }\n}\n\n// before creating the store:\nassertDbPathUsable(dbPath);","typeGuard":"function isRegularFileOrMissing(p) {\n  const st = fs.lstatSync(p, { throwIfNoEntry: false });\n  return st === undefined || st.isFile();\n}","tryCatchPattern":"try {\n  const store = await createStateStore({ dbPath });\n} catch (error) {\n  if (error.message.includes('database path is not a regular file')) {\n    const st = fs.lstatSync(dbPath, { throwIfNoEntry: false });\n    if (st && st.isDirectory()) fs.rmSync(dbPath, { recursive: true });\n    // retry\n  } else {\n    throw error;\n  }\n}","preventionTips":["Always pass a file path (ending in a filename like state.db), never a directory, as dbPath","Before first use, check with fs.lstatSync that an existing dbPath is a regular file and not a symlink","Avoid placing the database where sockets/FIFOs are created (IPC dirs, tmp mount points)","Give each concurrent process/test its own dbPath to prevent path collisions"],"tags":["filesystem","path-safety","state-store","validation"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}