{"id":"68ac2f914a80b151","repo":"expressjs/express","slug":"eisdir","errorCode":"EISDIR","errorMessage":"EISDIR, read","messagePattern":"EISDIR, read","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/response.js","lineNumber":943,"sourceCode":"  var done = false;\n  var streaming;\n\n  // request aborted\n  function onaborted() {\n    if (done) return;\n    done = true;\n\n    var err = new Error('Request aborted');\n    err.code = 'ECONNABORTED';\n    callback(err);\n  }\n\n  // directory\n  function ondirectory() {\n    if (done) return;\n    done = true;\n\n    var err = new Error('EISDIR, read');\n    err.code = 'EISDIR';\n    callback(err);\n  }\n\n  // errors\n  function onerror(err) {\n    if (done) return;\n    done = true;\n    callback(err);\n  }\n\n  // ended\n  function onend() {\n    if (done) return;\n    done = true;\n    callback();\n  }\n","sourceCodeStart":925,"sourceCodeEnd":961,"githubUrl":"https://github.com/expressjs/express/blob/a3714473feb3d2908add734d340e7755fd85e0a3/lib/response.js#L925-L961","documentation":"In the same `sendfile` helper, the `send` stream emits a `directory` event when the resolved path is a directory rather than a regular file. Express's `ondirectory` handler (lib/response.js:939-946) converts that into `new Error('EISDIR, read')` with `code: 'EISDIR'`, mimicking the Node.js fs error you would get from reading a directory. It means `res.sendFile()`/`res.download()` was pointed at a directory, which cannot be streamed as a file.","triggerScenarios":"`res.sendFile(path)` or `res.download(path)` where `path` resolves to a directory: passing the folder itself instead of a file inside it, building the path from user input or a route param that is empty/`.`/ends in a slash, or joining `root` with a relative segment that lands on a directory. Note Express does not follow directory-index behavior here — unlike `express.static`, sendFile never serves `index.html` for a directory.","commonSituations":"SPA fallbacks written as `res.sendFile(path.join(__dirname, 'build'))` (missing `'index.html'`); file-serving routes like `/files/:name` hit with an empty or dot name; deployment layouts where the expected file was replaced by a directory of the same name; refactors that changed a build output from a single bundle file to a folder.","solutions":["Point sendFile at an actual file — typically append the filename: `res.sendFile(path.join(buildDir, 'index.html'))`.","If serving user-derived paths, validate with `fs.stat` (or catch `err.code === 'EISDIR'` in the sendFile callback) and respond 404 for directories.","If you want directory-index behavior (serve index.html for a folder), use `express.static(dir)` instead of hand-rolled sendFile routes.","Check the deployed filesystem layout — confirm the target is a file, not a folder created by the build or extraction step."],"exampleFix":"// before\napp.get('*', function (req, res) {\n  res.sendFile(path.join(__dirname, 'build')); // 'build' is a directory -> EISDIR\n});\n\n// after\napp.get('*', function (req, res) {\n  res.sendFile(path.join(__dirname, 'build', 'index.html'));\n});","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nconst path = require('path');\n\nconst resolved = path.resolve(root, requestedName);\nconst stats = fs.statSync(resolved, { throwIfNoEntry: false });\nif (!stats || !stats.isFile()) {\n  return res.status(404).end(); // directory or missing — don't call sendFile\n}\nres.sendFile(resolved);","typeGuard":"function isEisdir(err) {\n  return err != null && typeof err === 'object' && err.code === 'EISDIR';\n}","tryCatchPattern":"res.sendFile(filePath, function (err) {\n  if (err) {\n    if (err.code === 'EISDIR' || err.code === 'ENOENT') {\n      return res.status(404).end(); // path is a directory or missing\n    }\n    next(err);\n  }\n});","preventionTips":["stat the resolved path and require stats.isFile() before calling res.sendFile — directories should map to a 404 (or an explicit index file), never reach sendFile","Never build sendFile paths directly from user input; resolve against a fixed root and reject paths that escape it or end with a separator","Map EISDIR to a 404 in the sendFile callback rather than leaking a 500 with filesystem details to the client","If directory URLs should serve an index page, append index.html explicitly (or use express.static with its index option) instead of passing the directory to sendFile","Add tests that request a known directory path (e.g. '/uploads/') to lock in the 404 behavior"],"tags":["filesystem","express","sendfile","path-resolution"],"analyzedSha":"a3714473feb3d2908add734d340e7755fd85e0a3","analyzedAt":"2026-07-31T18:55:36.968Z","schemaVersion":2}