expressjs/express · error · TypeError
path must be absolute or specify root to res.sendFile
Error message
path must be absolute or specify root to res.sendFile
What it means
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.
Source
Thrown at lib/response.js:395
var next = req.next;
var opts = options || {};
if (!path) {
throw new TypeError('path argument is required to res.sendFile');
}
if (typeof path !== 'string') {
throw new TypeError('path must be a string to res.sendFile')
}
// support function as second arg
if (typeof options === 'function') {
done = options;
opts = {};
}
if (!opts.root && !pathIsAbsolute(path)) {
throw new TypeError('path must be absolute or specify root to res.sendFile');
}
// create file stream
var pathname = encodeURI(path);
// wire application etag option to send
opts.etag = this.app.enabled('etag');
var file = send(req, pathname, opts);
// transfer
sendfile(res, file, opts, function (err) {
if (done) return done(err);
if (err && err.code === 'EISDIR') return next();
// next() all but write errors
if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
next(err);
}View on GitHub ↗ (pinned to a3714473fe)
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.
Example fix
// before
res.sendFile('index.html');
// after
res.sendFile('index.html', { root: path.join(__dirname, 'public') }); Defensive patterns
Strategy: validation
Validate before calling
const path = require('path');
const ROOT = path.resolve(__dirname, 'public');
res.sendFile(fileName, { root: ROOT }); // always pass root; fileName may be relative Type guard
function isAbsolutePath(p) {
return require('path').isAbsolute(p);
} Prevention
- 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
When it happens
Trigger: `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 `{}`).
Common situations: 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.
Related errors
- callback function required
- No default engine was specified and no extension was provide
- unknown value for etag function: ' + val
- unknown value for query parser function: ' + val
- path argument is required to res.sendFile
AI-assisted analysis of expressjs/express@a3714473fe (2026-07-31).
Data as JSON: /data/errors/01dab26970d006b8.json.
Report an issue: GitHub ↗.