paperclipai/paperclip · error · Error

ACPX runtime directory must not be a filesystem root

Error message

ACPX runtime directory must not be a filesystem root

What it means

resolveAcpxRuntimeRoot additionally refuses to use the filesystem root ('/' on POSIX, drive root on Windows) as the runtime base. Because per-session paths are built by appending 'acpx/<session-dir>' under the root, using the root would scatter runtime state across the top of the filesystem. The error is thrown when realpath(runtimeDirectory) equals dirname(realpath(runtimeDirectory)).

Source

Thrown at packages/paperclip-runner/src/drivers/acpx/recovery-identity.ts:309

  const metadata = await stat(workspacePath);
  if (!metadata.isDirectory() || workspacePath === dirname(workspacePath)) {
    throw new Error("ACPX working directory must be a non-root directory");
  }
  return workspacePath;
}

export async function resolveAcpxRuntimeRoot(
  runtimeDirectory: string,
  sessionId: string,
): Promise<string> {
  if (!runtimeDirectory.trim())
    throw new Error("ACPX runtime directory is required");
  const root = await realpath(runtimeDirectory);
  const metadata = await stat(root);
  if (!metadata.isDirectory())
    throw new Error("ACPX runtime directory must be a directory");
  if (root === dirname(root))
    throw new Error("ACPX runtime directory must not be a filesystem root");
  return join(
    resolve(root),
    "acpx",
    acpxRuntimeSessionDirectoryName(sessionId),
  );
}

/**
 * Return the stable, filesystem-safe directory name used for one normalized
 * ACPX session below the runtime's `acpx` namespace.
 */
export function acpxRuntimeSessionDirectoryName(sessionId: string): string {
  const readable = sessionId
    .replace(/[^a-zA-Z0-9._-]/g, "_")
    .replace(/^\.+$/, "session")
    .slice(0, 80);
  const suffix = createHash("sha256")
    .update(sessionId)

View on GitHub (pinned to 01ad858492)

Solutions

  1. Set runtimeDirectory to a dedicated, non-root directory (e.g. <data>/runtime).
  2. Check your env/config source: an empty or missing variable is probably falling back to "/" — fix the default.
  3. In containers, verify the data volume mounted correctly; a missing mount leaves the path resolving to root.
  4. Add a config-time check that the chosen directory is non-root before constructing the driver.

Example fix

// before
const dir = process.env.ACPX_RUNTIME_DIR ?? "/"; // root fallback
// after
const dir = process.env.ACPX_RUNTIME_DIR;
if (!dir || dir === "/") throw new Error("ACPX_RUNTIME_DIR must be a non-root directory");
Defensive patterns

Strategy: validation

Validate before calling

import { isAbsolute, resolve } from "node:path";
const root = resolve(runtimeDirectory);
if (root === "/" || /^[A-Z]:\\?$/i.test(root)) {
  throw new Error("ACPX runtime directory must not be the filesystem root");
}

Prevention

When it happens

Trigger: Calling runtimeRoot()/resolveAcpxRuntimeRoot with runtimeDirectory = "/" (or "C:\\", or a symlink resolving to the filesystem root).

Common situations: An unset/empty config variable defaulting to "/"; a shell variable like RUNTIME_DIR=${BASE:-/} expanding to the root; a container mount where the intended volume failed to mount, leaving only "/"; misconfigured Docker volume mapping.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/584f0c88a2ba35fe. Report an issue: GitHub.