{"record":{"id":"988c6e02e4485ad4","repo":"paperclipai/paperclip","slug":"acpx-runtime-directory-must-be-a-directory","errorCode":null,"errorMessage":"ACPX runtime directory must be a directory","messagePattern":"ACPX runtime directory must be a directory","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/acpx/recovery-identity.ts","lineNumber":307,"sourceCode":"  if (!value.trim()) throw new Error(\"ACPX working directory is required\");\n  const workspacePath = await realpath(value);\n  const metadata = await stat(workspacePath);\n  if (!metadata.isDirectory() || workspacePath === dirname(workspacePath)) {\n    throw new Error(\"ACPX working directory must be a non-root directory\");\n  }\n  return workspacePath;\n}\n\nexport async function resolveAcpxRuntimeRoot(\n  runtimeDirectory: string,\n  sessionId: string,\n): Promise<string> {\n  if (!runtimeDirectory.trim())\n    throw new Error(\"ACPX runtime directory is required\");\n  const root = await realpath(runtimeDirectory);\n  const metadata = await stat(root);\n  if (!metadata.isDirectory())\n    throw new Error(\"ACPX runtime directory must be a directory\");\n  if (root === dirname(root))\n    throw new Error(\"ACPX runtime directory must not be a filesystem root\");\n  return join(\n    resolve(root),\n    \"acpx\",\n    acpxRuntimeSessionDirectoryName(sessionId),\n  );\n}\n\n/**\n * Return the stable, filesystem-safe directory name used for one normalized\n * ACPX session below the runtime's `acpx` namespace.\n */\nexport function acpxRuntimeSessionDirectoryName(sessionId: string): string {\n  const readable = sessionId\n    .replace(/[^a-zA-Z0-9._-]/g, \"_\")\n    .replace(/^\\.+$/, \"session\")\n    .slice(0, 80);","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/acpx/recovery-identity.ts#L289-L325","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check what the path actually is (ls -la / stat) and point the config at a real directory.","Create the directory if it is missing, or remove the file occupying the path and mkdir the intended runtime directory.","Fix symlink targets so they reference directories.","Validate the path is a directory in your config loader before constructing the driver."],"exampleFix":"// before\nconst driver = createAcpxDriver({ runtimeDirectory: \"/var/lib/paperclip/runtime.json\" }); // a file\n// after\nawait mkdir(\"/var/lib/paperclip/runtime\", { recursive: true });\nconst driver = createAcpxDriver({ runtimeDirectory: \"/var/lib/paperclip/runtime\" });","handlingStrategy":"validation","validationCode":"import { statSync } from \"node:fs\";\nif (!statSync(runtimeDirectory).isDirectory()) {\n  throw new Error(`ACPX runtime path must be a directory: ${runtimeDirectory}`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Point runtimeDirectory at a dedicated directory, never a file or socket.","Create the directory with mkdir -p during provisioning.","Audit symlinks in the runtime path when migrating environments."],"tags":["filesystem","config","path","acpx"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}