{"id":"01dab26970d006b8","repo":"expressjs/express","slug":"path-must-be-absolute-or-specify-root-to-res-sendf","errorCode":null,"errorMessage":"path must be absolute or specify root to res.sendFile","messagePattern":"path must be absolute or specify root to res\\.sendFile","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/response.js","lineNumber":395,"sourceCode":"  var next = req.next;\n  var opts = options || {};\n\n  if (!path) {\n    throw new TypeError('path argument is required to res.sendFile');\n  }\n\n  if (typeof path !== 'string') {\n    throw new TypeError('path must be a string to res.sendFile')\n  }\n\n  // support function as second arg\n  if (typeof options === 'function') {\n    done = options;\n    opts = {};\n  }\n\n  if (!opts.root && !pathIsAbsolute(path)) {\n    throw new TypeError('path must be absolute or specify root to res.sendFile');\n  }\n\n  // create file stream\n  var pathname = encodeURI(path);\n\n  // wire application etag option to send\n  opts.etag = this.app.enabled('etag');\n  var file = send(req, pathname, opts);\n\n  // transfer\n  sendfile(res, file, opts, function (err) {\n    if (done) return done(err);\n    if (err && err.code === 'EISDIR') return next();\n\n    // next() all but write errors\n    if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {\n      next(err);\n    }","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/expressjs/express/blob/a3714473feb3d2908add734d340e7755fd85e0a3/lib/response.js#L377-L413","documentation":"Thrown by `res.sendFile` when the given path is relative and no `root` option was provided (`!opts.root && !pathIsAbsolute(path)`). Express requires an absolute path or an explicit root both to locate the file deterministically and as a safety measure — the root confines resolution and enables the send module's path-traversal protection.","triggerScenarios":"`res.sendFile('index.html')` or `res.sendFile('./views/page.html')` without `{ root: ... }`; passing options as the callback position so `root` is ignored (options given after a function second arg are replaced with `{}`).","commonSituations":"Copying examples that assume `root` was set; relying on process cwd; moving code between files and losing an `__dirname` join; in ESM modules where `__dirname` doesn't exist and the join was dropped.","solutions":["Add a root option: `res.sendFile('index.html', { root: path.join(__dirname, 'public') })`.","Or make the path absolute yourself: `res.sendFile(path.join(__dirname, 'public', 'index.html'))`.","In ESM, derive a dirname first: `const __dirname = path.dirname(fileURLToPath(import.meta.url))`.","Prefer the root option over pre-joining when the filename comes from user input, so traversal protection applies."],"exampleFix":"// before\nres.sendFile('index.html');\n// after\nres.sendFile('index.html', { root: path.join(__dirname, 'public') });","handlingStrategy":"validation","validationCode":"const path = require('path');\nconst ROOT = path.resolve(__dirname, 'public');\nres.sendFile(fileName, { root: ROOT }); // always pass root; fileName may be relative","typeGuard":"function isAbsolutePath(p) {\n  return require('path').isAbsolute(p);\n}","tryCatchPattern":null,"preventionTips":["Always pass the root option to res.sendFile — it both satisfies this check and gives you path-traversal protection","If not using root, build paths with path.resolve so they are guaranteed absolute","Never concatenate user input into an absolute path; keep user input relative and let root confine it"],"tags":["express","sendfile","path","configuration"],"analyzedSha":"a3714473feb3d2908add734d340e7755fd85e0a3","analyzedAt":"2026-07-31T18:55:36.968Z","schemaVersion":2}