process.chdir()

Changes the current working directory of the Node.js process to the given path.

Signature
process.chdir(directory)
  • Added in Node.js v0.1.17

Parameters

ParameterTypeDescription
$directorystringPath of the directory to move into. Relative paths are resolved against the current working directory.

Return value

undefinedNothing; the change takes effect immediately for the whole process.

Errors

Throws if the change fails — ENOENT when the directory does not exist, ENOTDIR when the path is a file, EACCES when it is not readable — and it is not available in Worker threads.

process.chdir() moves the whole process to another directory, exactly like cd in a shell. Every relative path used afterwards — fs calls, path.resolve(), child processes you spawn — resolves against the new location.

When to reach for it

Realistically, rarely. Legitimate uses are narrow: a task runner that must emulate running inside a package directory, or a tool wrapping a library that only accepts relative paths. Almost everything else is better served by building an absolute path with path.resolve(), or by passing a cwd option — child_process.spawn(), fs.readdir() globs and most build tools all accept one.

What trips people up

  • It is process-global. There is one working directory shared by every module and every pending callback. Change it inside an async function and unrelated code that resumes in between suddenly resolves paths somewhere unexpected — a class of bug that is very hard to reproduce.
  • It throws. A missing directory raises ENOENT, so wrap the call or check first.
  • It is unavailable in Worker threads, because the working directory belongs to the OS process, not the thread.

If you must change directory, save the old one and restore it in a finally block.

Example

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

When to use it

  • Emulate running a build tool from inside a package directory in a monorepo script.
  • Enter a temporary scratch directory before generating files, then restore the previous directory.
  • Work with a legacy library whose API only accepts paths relative to the working directory.
  • Reproduce a shell workflow that assumes a specific starting directory.

Frequently asked questions

Why does process.chdir() throw ERR_WORKER_UNSUPPORTED_OPERATION?

Because the working directory belongs to the operating system process, not to an individual thread, so Node refuses to change it from inside a Worker where the effect would leak to every other thread. Resolve absolute paths in the worker instead, or do the directory change on the main thread before starting it.

How do I change directory in Node without affecting the whole process?

You cannot — there is exactly one working directory per process. Use path.resolve() to build absolute paths, or pass a `cwd` option to child_process.spawn(), fs.readdir(), or your build tool, which scopes the directory to that one operation.

What error does process.chdir() throw for a path that does not exist?

An error with code ENOENT, the same code the underlying chdir(2) system call returns. A path that exists but is a file gives ENOTDIR, and a directory you lack permission to enter gives EACCES, so check err.code rather than matching the message.

More examples

Restoring the previous directory with try/finally

The output shows the directory listing coming from the nested package while the process ends up back where it started. The `finally` block is what makes this safe: if `fn()` throws, the directory is still restored before the error propagates, so a failure in one step does not leave every later relative path pointing at the wrong folder. Note the callback must be synchronous — an `await` inside it would let unrelated code run while the directory is switched.

Example · javascript
function withDirectory(dir, fn) {
  const previous = process.cwd();
  process.chdir(dir);
  try {
    return fn();
  } finally {
    process.chdir(previous);
  }
}

const fs = require('node:fs');

const names = withDirectory('packages/core', () => {
  // Relative paths inside the callback resolve against packages/core.
  return fs.readdirSync('.').sort();
});

console.log('entries:', names);
console.log('restored:', process.cwd().endsWith('core') === false);
// entries: [ 'package.json', 'src', 'test' ]
// restored: true

Preferring a cwd option over chdir

The output shows the child running in the package directory while the parent's own working directory is untouched. This is the general escape hatch: `child_process`, `fs.readdir` with globs, and most build tool APIs take a `cwd` option, which confines the change to a single operation instead of mutating shared state that every concurrent task in the process can observe.

Example · javascript
const { spawnSync } = require('node:child_process');
const path = require('node:path');

const pkgDir = path.resolve('packages/core');

// Fragile: global state, and anything async in between sees the change.
// process.chdir(pkgDir);
// spawnSync('npm', ['test'], { stdio: 'inherit' });

// Preferred: scope the directory to the one call that needs it.
const result = spawnSync('npm', ['test'], { cwd: pkgDir, stdio: 'inherit' });

console.log('exit code:', result.status);
console.log('process directory unchanged:', process.cwd() !== pkgDir);
// exit code: 0
// process directory unchanged: true

Discussion

  • Be the first to comment on this lesson.