paperclipai/paperclip · error · Error

ACPX runtime directory must be a directory

Error message

ACPX runtime directory must be a directory

What it means

resolveAcpxRuntimeRoot resolves and validates the base runtime directory used to build per-session ACPX runtime paths. After resolving symlinks with realpath and stat-ing the path, it requires the target to actually be a directory. This error is thrown when the configured runtimeDirectory path exists but is a file, socket, or other non-directory entry.

Source

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

  if (!value.trim()) throw new Error("ACPX working directory is required");
  const workspacePath = await realpath(value);
  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);

View on GitHub (pinned to 01ad858492)

Solutions

  1. Check what the path actually is (ls -la / stat) and point the config at a real directory.
  2. Create the directory if it is missing, or remove the file occupying the path and mkdir the intended runtime directory.
  3. Fix symlink targets so they reference directories.
  4. Validate the path is a directory in your config loader before constructing the driver.

Example fix

// before
const driver = createAcpxDriver({ runtimeDirectory: "/var/lib/paperclip/runtime.json" }); // a file
// after
await mkdir("/var/lib/paperclip/runtime", { recursive: true });
const driver = createAcpxDriver({ runtimeDirectory: "/var/lib/paperclip/runtime" });
Defensive patterns

Strategy: validation

Validate before calling

import { statSync } from "node:fs";
if (!statSync(runtimeDirectory).isDirectory()) {
  throw new Error(`ACPX runtime path must be a directory: ${runtimeDirectory}`);
}

Prevention

When it happens

Trigger: Calling runtimeRoot()/resolveAcpxRuntimeRoot with a runtimeDirectory that realpath+stat resolves to a non-directory — e.g. the config points at a regular file, a symlink to a file, or a Unix socket path.

Common situations: Config value accidentally set to a file (e.g. a config file inside the runtime dir); a symlink that was repointed to a file; leftover artifact where a directory was replaced by a file during a failed cleanup or migration.

Related errors


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