fsPromises.access()
Tests whether the process can access a path with the given permissions, rejecting if it cannot.
fsPromises.access(path[, mode])- Added in Node.js v10.0.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$path | string | Buffer | URL | Path to test. |
$modeoptional | integer= fs.constants.F_OK | Accessibility checks to perform: fs.constants.F_OK, R_OK, W_OK and X_OK, combined with bitwise OR. |
Return value
Promise<undefined>Fulfills with undefined if the file is accessible; otherwise the promise rejects.
Errors
Rejects with ENOENT when the path does not exist and EACCES when the requested permission bits are not granted; the rejection is the result, not an exceptional case.
Changelog
- PHP v10.0.0Added in v10.0.0. Note that fs.exists() has been deprecated since v1.0.0 and fs.promises has no existsSync counterpart — access is the promise-based check.
fsPromises.access() asks the operating system whether the current process could perform a class of operation on a path. It resolves with undefined on success and rejects otherwise — there is no boolean, so a try/catch is the API.
There is no promises existsSync
node:fs/promises deliberately ships no exists(). The idiomatic replacement is access(path, constants.F_OK) wrapped in a small helper that turns the rejection into false.
What trips people up
- Do not use it as a guard before opening a file. Node's own documentation says so. Between the check and the open, another process can delete or chmod the file, and you handle the error twice for no benefit. Open it and catch
ENOENT. - The modes are bit flags from
require('node:fs').constants, not fromfs/promises. Passing the number4works but is unreadable. W_OKon a directory means "can create entries here", which is what you actually want before writing a new file into it.- Running as root makes
R_OK/W_OKsucceed on almost everything, so tests pass in a container and fail in production.
Example
When to use it
- Report a clear startup error when a config file the user named is missing.
- Check that an output directory is writable before a long job begins.
- Verify an optional plugin file is present before requiring it.
- Implement an exists() helper for code that genuinely only needs a boolean.
Frequently asked questions
How do I check if a file exists with fs promises?
Wrap fsPromises.access(path, constants.F_OK) in try/catch and return true from the try and false from the catch — there is no existsSync in node:fs/promises.
Why does fsPromises.access have no return value?
It signals the result through the promise: fulfilled with undefined means accessible, rejected with ENOENT or EACCES means not. A boolean would lose the reason for the failure.
Is fsPromises.access safe to use before writing a file?
Only as a fail-fast diagnostic, never as a correctness guard. The permissions can change between the check and the write, so the write still needs its own error handling — at which point the check is redundant.
More examples
Check a directory is writable before starting work
Prints the writable message, then missing directory: ENOENT. Checking W_OK on the directory up front turns a failure that would otherwise surface after minutes of work into an immediate, readable error.
import fs from 'node:fs/promises';
import { constants } from 'node:fs';
await fs.mkdir('output', { recursive: true });
try {
await fs.access('output', constants.W_OK);
console.log('output/ is writable, starting job');
} catch (err) {
console.log('cannot write to output/:', err.code);
process.exitCode = 1;
}
try {
await fs.access('nowhere', constants.W_OK);
} catch (err) {
console.log('missing directory:', err.code);
}
await fs.rm('output', { recursive: true });Why the check-then-open pattern is worse than just opening
Both branches print ENOENT. The first shows the race the guard cannot close; the second does the same job in one syscall with one catch, which is the pattern the Node docs recommend.
import fs from 'node:fs/promises';
import { constants } from 'node:fs';
await fs.writeFile('doomed.txt', 'here for now');
// Anti-pattern: check, then act.
await fs.access('doomed.txt', constants.R_OK);
await fs.rm('doomed.txt'); // stands in for another process
try {
await fs.readFile('doomed.txt', 'utf8');
} catch (err) {
console.log('check passed but read failed:', err.code);
}
// Better: one operation, one error path.
try {
const data = await fs.readFile('doomed.txt', 'utf8');
console.log(data);
} catch (err) {
console.log('handled directly:', err.code);
}Distinguishing missing from forbidden
Prints exists? ok, readable? EACCES (unless the process runs as root, where it prints ok), absent file: ENOENT. Branching on err.code is how you tell the user "not found" from "not allowed".
import fs from 'node:fs/promises';
import { constants } from 'node:fs';
await fs.writeFile('secret.txt', 'shh', { mode: 0o000 });
async function why(p, mode) {
try {
await fs.access(p, mode);
return 'ok';
} catch (err) {
return err.code;
}
}
console.log('exists?', await why('secret.txt', constants.F_OK));
console.log('readable?', await why('secret.txt', constants.R_OK));
console.log('absent file:', await why('gone.txt', constants.F_OK));
await fs.chmod('secret.txt', 0o600);
await fs.rm('secret.txt');
Discussion