affaan-m/ECC · error · Error

Unsafe state-store path

Error message

Unsafe state-store path '${dbPath}': database path is not a regular file

What it means

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.

Solutions

  1. 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
  2. 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
  3. If the path is a socket/FIFO from another process, stop that process or relocate the store with a different dbPath
  4. If a mount shadows the path, unmount or re-point the mount and retry

Example fix

// before: dbPath points at a directory that exists
createStateStore({ dbPath: '/var/lib/ecc' });
// after: point at a regular file inside that directory
import path from 'path';
createStateStore({ dbPath: path.join('/var/lib/ecc', 'state.db') });
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'fs';

export function assertDbPathUsable(dbPath) {
  const st = fs.lstatSync(dbPath, { throwIfNoEntry: false });
  if (st && st.isSymbolicLink()) {
    throw new Error(`dbPath '${dbPath}' is a symlink; refusing to use it`);
  }
  if (st && !st.isFile()) {
    throw new Error(`dbPath '${dbPath}' exists but is not a regular file (${st.isDirectory() ? 'directory' : 'special file'})`);
  }
}

// before creating the store:
assertDbPathUsable(dbPath);

Type guard

function isRegularFileOrMissing(p) {
  const st = fs.lstatSync(p, { throwIfNoEntry: false });
  return st === undefined || st.isFile();
}

Try / catch

try {
  const store = await createStateStore({ dbPath });
} catch (error) {
  if (error.message.includes('database path is not a regular file')) {
    const st = fs.lstatSync(dbPath, { throwIfNoEntry: false });
    if (st && st.isDirectory()) fs.rmSync(dbPath, { recursive: true });
    // retry
  } else {
    throw error;
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/6c2d9628e04bf19c. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/state-store/index.js:106

        }
      }
      stats = fs.lstatSync(currentPath);
      assertNotSymlink(currentPath, stats);
    }

    if (!stats.isDirectory() && !isAllowedPlatformSymlink(currentPath, stats)) {
      throw stateStorePathError(currentPath, 'an intermediate component is not a directory');
    }
  }

  return absolutePath;
}

function assertSafeDatabaseFile(dbPath) {
  const stats = lstatIfPresent(dbPath);
  assertNotSymlink(dbPath, stats);
  if (stats && !stats.isFile()) {
    throw stateStorePathError(dbPath, 'database path is not a regular file');
  }
  return stats;
}

function readDatabaseFile(dbPath) {
  assertSafeDatabaseFile(dbPath);
  const noFollow = fs.constants.O_NOFOLLOW || 0;
  const fileDescriptor = fs.openSync(dbPath, fs.constants.O_RDONLY | noFollow);
  try {
    const stats = fs.fstatSync(fileDescriptor);
    if (!stats.isFile()) {
      throw stateStorePathError(dbPath, 'database path is not a regular file');
    }
    return fs.readFileSync(fileDescriptor);
  } finally {
    fs.closeSync(fileDescriptor);
  }
}

View on GitHub (pinned to 8321021c54)