fsPromises.access()

Tests whether the process can access a path with the given permissions, rejecting if it cannot.

Signature
fsPromises.access(path[, mode])
  • Added in Node.js v10.0.0

Parameters

ParameterTypeDescription
$pathstring | Buffer | URLPath to test.
$modeoptionalinteger= fs.constants.F_OKAccessibility 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 from fs/promises. Passing the number 4 works but is unreadable.
  • W_OK on 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_OK succeed on almost everything, so tests pass in a container and fail in production.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

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.

Example · javascript
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.

Example · javascript
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".

Example · javascript
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

  • Be the first to comment on this lesson.