{"record":{"id":"59381cba5d48eea6","repo":"vercel/turborepo","slug":"error-removing-node-modules","errorCode":"error_removing_node_modules","errorMessage":"Failed to remove node_modules","messagePattern":"Failed to remove node_modules","errorType":"exception","errorClass":"ConvertError","httpStatus":null,"severity":"error","filePath":"packages/turbo-workspaces/src/managers/bun.ts","lineNumber":216,"sourceCode":"  });\n\n  if (!options?.dry) {\n    fs.writeJSONSync(project.paths.packageJson, packageJson, { spaces: 2 });\n\n    // collect all workspace node_modules directories\n    const allModulesDirs = [\n      project.paths.nodeModules,\n      ...project.workspaceData.workspaces.map((w) => w.paths.nodeModules)\n    ];\n    try {\n      logger.subStep(`removing \"node_modules\"`);\n      await Promise.all(\n        allModulesDirs.map((dir) =>\n          fs.rm(dir, { recursive: true, force: true })\n        )\n      );\n    } catch (err) {\n      throw new ConvertError(\"Failed to remove node_modules\", {\n        type: \"error_removing_node_modules\"\n      });\n    }\n  }\n}\n\n/**\n * Clean is called post install, and is used to clean up any files\n * from this package manager that were needed for install,\n * but not required after migration\n */\n// eslint-disable-next-line @typescript-eslint/require-await -- must match the clean type signature\nasync function clean(args: CleanArgs): Promise<void> {\n  const { project, logger, options } = args;\n\n  logger.subStep(\n    `removing ${path.relative(project.paths.root, project.paths.lockfile)}`\n  );","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/vercel/turborepo/blob/9f94a7d215b3881942527dd526afa3fc650869d5/packages/turbo-workspaces/src/managers/bun.ts#L198-L234","documentation":"During bun removal (a step of converting away from bun), all root and workspace node_modules directories are deleted with fs.rm(dir, { recursive: true, force: true }). Any rejection is rethrown as ConvertError type error_removing_node_modules; the underlying errno (EBUSY/EPERM on Windows, EACCES on Unix) is discarded by the wrapper.","triggerScenarios":"fs.rm failing on any node_modules path: files locked by a running dev server, editor, or antivirus (Windows EBUSY/EPERM); directories owned by another user (root-created node_modules from Docker); a concurrent install touching the same tree.","commonSituations":"Converting while `next dev`/VS Code's TS server holds files open; converting in WSL against /mnt/c where file locks behave badly; node_modules created by root in CI then manipulated as a normal user.","solutions":["Stop dev servers, editors, and watchers, then re-run the conversion","Remove leftovers manually: rm -rf node_modules and each workspace's node_modules listed in project.workspaceData, then retry","Fix ownership/permissions (chown -R $(whoami) node_modules) if another user/container created them","On Windows, wait a few seconds for handles to be released and retry"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import { access, constants } from \"node:fs/promises\";\n\nasync function isRemovable(dir: string): Promise<boolean> {\n  try {\n    await access(dir, constants.W_OK);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nfor (const dir of [project.paths.nodeModules, ...project.workspaceData.workspaces.map((w) => w.paths.nodeModules)]) {\n  if (!(await isRemovable(dir))) console.warn(`Not removable: ${dir}`);\n}","typeGuard":"function isNodeModulesRemovalError(e: unknown): boolean {\n  return e instanceof ConvertError && e.type === \"error_removing_node_modules\";\n}","tryCatchPattern":"try {\n  await convertProject({ project, convertTo, logger });\n} catch (e) {\n  if (e instanceof ConvertError && e.type === \"error_removing_node_modules\") {\n    // kill dev servers/editors, then finish cleanup manually:\n    // rm -rf node_modules <each workspace>/node_modules\n    // and re-run with options.skipInstall if the conversion already wrote its files\n  } else throw e;\n}","preventionTips":["Stop dev servers, editors, and file watchers before converting package managers","On Windows, expect locked handles — retry removal after a short delay","Ensure the converting process owns node_modules (avoid converting trees created by root/Docker)"],"tags":["package-manager","bun","filesystem","node-modules","permissions"],"backgroundTag":"file-removal-failed","analyzedSha":"9f94a7d215b3881942527dd526afa3fc650869d5","analyzedAt":"2026-08-16T19:47:29.531Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}