{"record":{"id":"6c7a79dd289a7a15","repo":"denoland/deno","slug":"err-fs-cp-non-dir-to-dir","errorCode":"ERR_FS_CP_NON_DIR_TO_DIR","errorMessage":"Cannot overwrite directory with non-directory","messagePattern":"Cannot overwrite directory with non-directory","errorType":"exception","errorClass":"SystemError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_fs/cp/cp.ts","lineNumber":76,"sourceCode":"  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,\n        syscall: \"cp\",\n        errno: EEXIST,\n        code: \"EEXIST\",\n      });\n    case \"EISDIR\":\n      throw new ERR_FS_EISDIR({\n        message: err.message,\n        path: err.path,","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/_fs/cp/cp.ts#L58-L94","documentation":"The mirror case of DIR_TO_NON_DIR: fs.cp stats both paths and, when src is a non-directory (a file, socket, etc.) while an existing dest is a directory, rejects with kind NON_DIR_TO_DIR. throwCpError maps it to ERR_FS_CP_NON_DIR_TO_DIR ('Cannot overwrite directory with non-directory', errno ENOTDIR). Overwriting a directory with a single file is not a defined copy operation, so it fails before any transfer.","triggerScenarios":"fs.cp('notes.txt', 'notes/') where 'notes' is an existing directory; forgetting { recursive: true } is NOT the fix here — the src/dest kinds simply conflict; deploy pipelines copying a versioned file (app.js) onto a directory path that a previous layout created.","commonSituations":"Changing artifact layout from a directory to a single bundled file without cleaning old builds; trailing-slash confusion where the dest directory already exists; templates that create directory skeletons later targeted by file copies.","solutions":["Pick matching kinds: copy files onto file paths, directories (with recursive) onto directory paths","If the old directory is stale, remove it first: fs.rm(dest, { recursive: true, force: true })","Validate with fs.stat before copying and fail fast with a clear message"],"exampleFix":"// before\nfs.cp('bundle.js', 'out/', cb); // 'out' is an existing directory\n\n// after\nfs.cp('bundle.js', 'out/bundle.js', cb);\n// or clear the stale directory first\nfs.rmSync('out', { recursive: true, force: true });\nfs.cp('bundle.js', 'out', cb);","handlingStrategy":"try-catch","validationCode":"const st = fs.statSync(dest, { throwIfNoEntry: false });\nif (st?.isDirectory()) throw new Error('cp: dest is a directory but src is a file');","typeGuard":null,"tryCatchPattern":"try { fs.copyFileSync(src, dest); } catch (e) { if (e.code === 'ERR_FS_CP_NON_DIR_TO_DIR') { fs.rmSync(dest, { recursive: true }); fs.copyFileSync(src, dest); } else throw e; }","preventionTips":["Match artifact kind to layout: bundles are files, folders are folders","Remove old build trees when the artifact shape changes","Check dest with lstat when symlinks may be involved"],"tags":["node-compat","fs","cp","copy","err-fs-cp-non-dir-to-dir","enotdir","deno"],"backgroundTag":"fs-copy-type-mismatch","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}