{"record":{"id":"f6d4e6a70b7b84d5","repo":"denoland/deno","slug":"eisdir","errorCode":"EISDIR","errorMessage":"${src} is a directory (not copied)","messagePattern":"(.+?) is a directory \\(not copied\\)","errorType":"exception","errorClass":"ERR_FS_EISDIR","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_fs/cp/cp.ts","lineNumber":193,"sourceCode":"    opts.mode === 0;\n}\n\nfunction getStatsForCopy(\n  statInfo,\n  src,\n  dest,\n  opts,\n) {\n  const { EISDIR, EINVAL } = lazyConstants();\n  if ((statInfo & CpEntryFlags.IsSrcDirectory) && opts.recursive) {\n    return onDir(statInfo, src, dest, opts);\n  } else if (statInfo & CpEntryFlags.IsSrcDirectory) {\n    throw new ERR_FS_EISDIR({\n      message: `${src} is a directory (not copied)`,\n      path: src,\n      syscall: \"cp\",\n      errno: EISDIR,\n      code: \"EISDIR\",\n    });\n  } else if (\n    (statInfo & CpEntryFlags.IsSrcFile) ||\n    (statInfo & CpEntryFlags.IsSrcCharDevice) ||\n    (statInfo & CpEntryFlags.IsSrcBlockDevice)\n  ) {\n    return onFile(statInfo, src, dest, opts);\n  } else if (statInfo & CpEntryFlags.IsSrcSymlink) {\n    const isDestExists = !!(statInfo & CpEntryFlags.IsDestExists);\n    return onLink(isDestExists, src, dest, opts);\n  } else if (statInfo & CpEntryFlags.IsSrcSocket) {\n    throw new ERR_FS_CP_SOCKET({\n      message: `cannot copy a socket file: ${dest}`,\n      path: dest,\n      syscall: \"cp\",\n      errno: EINVAL,\n      code: \"EINVAL\",\n    });","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/_fs/cp/cp.ts#L175-L211","documentation":"Thrown directly by the JS slow path of Deno's cp polyfill when src is a directory and options.recursive is falsy: '<src> is a directory (not copied)' with code EISDIR (cp.ts:187-194). This path runs when the options disable the native fast path — i.e. when filter, dereference: true, force: false, errorOnExist: true, preserveTimestamps: true, verbatimSymlinks: true, or a nonzero mode is set (canUseNativeFastPath, cp.ts:168-176). Same rule as the Rust-detected variant: directories require an explicit recursive opt-in.","triggerScenarios":"fs.cp(dir, dest, { dereference: true }) without recursive; fs.cp(dir, dest, { filter: fn }) or { preserveTimestamps: true } forgetting recursive; any option combination that forces the JS entry loop.","commonSituations":"Symlink-resolving deploys (dereference) or timestamp-preserving backups that forgot recursive; filter-based partial copies over directories; code that only tested the default-options fast path.","solutions":["Add recursive: true to the same options object.","If only files should be copied, iterate entries and dispatch files individually (your filter then drives selection).","lstat src up front and fail fast with your own error if a directory arrives without recursive."],"exampleFix":"// before\nawait fs.promises.cp('./site', './out', { dereference: true });\n// EISDIR: ./site is a directory (not copied)\n\n// after\nawait fs.promises.cp('./site', './out', { recursive: true, dereference: true });","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nasync function cpTyped(src, dest, opts = {}) {\n  const isDir = (await fs.promises.lstat(src)).isDirectory();\n  if (isDir && !opts.recursive) {\n    throw new Error(`cp of directory ${src} requires { recursive: true }`);\n  }\n  return fs.promises.cp(src, dest, { ...opts, recursive: isDir ? true : opts.recursive });\n}","typeGuard":null,"tryCatchPattern":"try {\n  await fs.promises.cp(dir, dest, { dereference: true });\n} catch (err) {\n  if (err.code === 'EISDIR' && err.syscall === 'cp') {\n    await fs.promises.cp(dir, dest, { recursive: true, dereference: true });\n  } else throw err;\n}","preventionTips":["Remember that dereference/filter/preserveTimestamps/force:false switch to the JS path — recursive is still required for dirs","Test option-rich copies against directories, not just files","Centralize cp option assembly in one helper so recursive is never forgotten"],"tags":["deno","node-compat","fs","cp","eisdir"],"backgroundTag":"is-a-directory","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}