{"record":{"id":"fcb5400d1dc3d4c5","repo":"paperclipai/paperclip","slug":"refusing-to-remove-instance-directory-configure","errorCode":null,"errorMessage":"Refusing to remove instance directory \"${configuredInstanceRoot}\" because its canonical path changed during cleanup.","messagePattern":"Refusing to remove instance directory \"(.+?)\" because its canonical path changed during cleanup\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/workspace-instance-cleanup.ts","lineNumber":381,"sourceCode":"      refusalReason: \"canonical_path_outside_managed_instances_dir\",\n    });\n    return { status: \"refused\", instanceRoot: configuredInstanceRoot, warning };\n  }\n\n  const dependencies = input.dependencies ?? defaultCleanupDependencies;\n  let postgresStopped = false;\n  const cleanup = async () => {\n    postgresStopped = await dependencies.stopEmbeddedPostgres(path.join(canonicalInstanceRoot, \"db\"));\n    const [currentManagedInstancesDir, currentInstanceRoot] = await Promise.all([\n      fs.realpath(managedInstancesDir),\n      fs.realpath(configuredInstanceRoot),\n    ]);\n    if (\n      currentManagedInstancesDir !== canonicalManagedInstancesDir\n      || currentInstanceRoot !== canonicalInstanceRoot\n      || !isStrictChildPath(currentInstanceRoot, currentManagedInstancesDir)\n    ) {\n      throw new Error(`Refusing to remove instance directory \"${configuredInstanceRoot}\" because its canonical path changed during cleanup.`);\n    }\n    await dependencies.removeInstanceRoot(currentInstanceRoot);\n  };\n\n  if (input.recorder) {\n    await input.recorder.recordOperation({\n      phase: \"workspace_teardown\",\n      cwd: input.workspacePath,\n      metadata: {\n        workspaceId: input.workspaceId,\n        workspacePath: input.workspacePath,\n        instanceRoot: canonicalInstanceRoot,\n        managedInstancesDir: canonicalManagedInstancesDir,\n        cleanupAction: \"remove_worktree_instance\",\n      },\n      run: async () => {\n        await cleanup();\n        return {","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/server/src/services/workspace-instance-cleanup.ts#L363-L399","documentation":"Mid-cleanup, re-resolving canonical paths showed the instance directory's realpath (or the managed dir's) changed since cleanup started, so the deletion is refused. The guard defends against symlink/race swaps deleting the wrong directory.","triggerScenarios":"Thrown at server/src/services/workspace-instance-cleanup.ts:356 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["The instance directory's canonical path changed mid-cleanup (possible symlink race). Re-run cleanup after verifying the path is stable and not a symlink attack."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}