{"record":{"id":"61a5d2c684424f71","repo":"denoland/deno","slug":"err-fs-cp-dir-to-non-dir","errorCode":"ERR_FS_CP_DIR_TO_NON_DIR","errorMessage":"Cannot overwrite non-directory with directory","messagePattern":"Cannot overwrite non-directory with directory","errorType":"exception","errorClass":"NodeSystemError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_fs/cp/cp.ts","lineNumber":68,"sourceCode":"const {\n  Number,\n  PromiseResolve,\n  SymbolAsyncIterator,\n} = primordials;\n\nfunction throwCpError(err) {\n  const { EEXIST, EINVAL, EISDIR, ENOTDIR } = lazyConstants();\n  switch (err.kind) {\n    case \"EINVAL\":\n      throw new ERR_FS_CP_EINVAL({\n        message: err.message,\n        path: err.path,\n        syscall: \"cp\",\n        errno: EINVAL,\n        code: \"EINVAL\",\n      });\n    case \"DIR_TO_NON_DIR\":\n      throw new ERR_FS_CP_DIR_TO_NON_DIR({\n        message: err.message,\n        path: err.path,\n        syscall: \"cp\",\n        errno: EISDIR,\n        code: \"EISDIR\",\n      });\n    case \"NON_DIR_TO_DIR\":\n      throw new ERR_FS_CP_NON_DIR_TO_DIR({\n        message: err.message,\n        path: err.path,\n        syscall: \"cp\",\n        errno: ENOTDIR,\n        code: \"ENOTDIR\",\n      });\n    case \"EEXIST\":\n      throw new ERR_FS_CP_EEXIST({\n        message: err.message,\n        path: err.path,","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/_fs/cp/cp.ts#L50-L86","documentation":"ERR_FS_CP_DIR_TO_NON_DIR is thrown by throwCpError when the native cp validation reports kind 'DIR_TO_NON_DIR': the source is a directory but the existing destination is a non-directory (a file), so the copy would have to overwrite a file with a directory tree, which fs.cp refuses.","triggerScenarios":"await fsp.cp('./src', './src.txt', { recursive: true }) where src is a directory and src.txt an existing regular file; deploying a directory over a stale file left at the destination path.","commonSituations":"Build outputs where an earlier run produced a file and a later run produces a directory at the same path; wrong CLI arg order swapping a file dest and dir src.","solutions":["Remove or rename the existing file at dest first (fs.rm(dest, { force: true }))","Point dest at a directory path that does not currently exist as a file","If automated, pre-stat dest and clean conflicting non-directories before copying"],"exampleFix":"// before\nawait fsp.cp(srcDir, destPath, { recursive: true }); // destPath is an existing file\n\n// after\nconst st = await fsp.lstat(destPath).catch(() => null);\nif (st && !st.isDirectory()) await fsp.rm(destPath, { force: true });\nawait fsp.cp(srcDir, destPath, { recursive: true });","handlingStrategy":"validation","validationCode":"const st = await fsp.lstat(dest).catch(() => null);\nif (st && !st.isDirectory()) await fsp.rm(dest, { force: true });\nawait fsp.cp(src, dest, { recursive: true });","typeGuard":"async function isDir(p) { try { return (await fsp.lstat(p)).isDirectory(); } catch { return false; } }","tryCatchPattern":"try { await fsp.cp(src, dest, { recursive: true }); } catch (e) { if (e.code === 'ERR_FS_CP_DIR_TO_NON_DIR') { await fsp.rm(dest, { force: true }); await fsp.cp(src, dest, { recursive: true }); } else throw e; }","preventionTips":["Pre-stat dest before directory copies and clear non-directory conflicts","Keep build outputs type-stable per path across runs","In deploy scripts, clean stale artifacts at conflicting paths"],"tags":["node-compat","fs","cp","copy","directory"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}