ESLint 'process'/'require'/'module' is not defined (tested)

error 'process' is not defined no-undef (and its siblings 'require' is not defined, 'module' is not defined) means ESLint parsed your file as plain JavaScript with no knowledge that it runs in Node.js. ESLint does not assume any runtime by default — it has no idea process, require, module, __dirname or exports exist unless you tell it. We reproduced this on ESLint v9.39.5 and measured the exact fix for both config formats in use today.

1. The exact error we reproduced

A minimal CommonJS file:

const fs = require('fs');

function readEnv() {
  return process.env.NODE_ENV;
}

module.exports = readEnv;

Running npx eslint bad.js with a bare flat config (eslint.config.js exporting [{ rules: { "no-undef": "error" } }], no globals set) on ESLint v9.39.5 produced:

/tmp/eslint-repro/bad.js
  1:12  error  'require' is not defined  no-undef
  4:10  error  'process' is not defined  no-undef
  7:1   error  'module' is not defined   no-undef

✖ 3 problems (3 errors, 0 warnings)

Exit code 1. All three globals are genuinely undefined as far as ESLint's static analysis is concerned — it never loads Node.js, so it has no built-in list of Node globals unless one is configured.

2. Why this happens: ESLint has zero environment knowledge by default

ESLint lints JavaScript syntax and the no-undef rule flags any identifier that isn't a declared variable, a function parameter, or a known global. "Known global" is not auto-detected from your runtime — ESLint ships with no preset environment at all. Browser code gets the same treatment: an undeclared window or document fails the identical way. The fix in both cases is the same: tell ESLint which globals exist for the environment you're actually targeting.

3. The fix on ESLint v9+ (flat config)

ESLint 9 uses flat config (eslint.config.js) by default. Add Node's globals via the languageOptions.globals key. We installed the globals npm package (the same list ESLint's own docs point to) and confirmed the fix:

// eslint.config.js
const globals = require("globals");

module.exports = [
  {
    languageOptions: {
      globals: globals.node
    },
    rules: {
      "no-undef": "error"
    }
  }
];

Re-running npx eslint --config eslint.config.js bad.js on the same bad.js: exit code 0, zero errors. globals.node is a plain object (keys are the global names, e.g. process, require, module, __dirname, exports) — you can also hand-write the same object yourself without the dependency if you only need a handful of names.

4. The fix on legacy .eslintrc (pre-v9 / ESLINT_USE_FLAT_CONFIG=false)

Projects still on the old .eslintrc.json format use the env key instead. We confirmed both states on the same v9.39.5 install by forcing legacy mode:

// .eslintrc.json — WITHOUT env.node: same 3 errors as flat config
{
  "parserOptions": { "ecmaVersion": 2021, "sourceType": "script" },
  "rules": { "no-undef": "error" }
}
// .eslintrc.json — WITH env.node: exit code 0
{
  "env": { "node": true },
  "parserOptions": { "ecmaVersion": 2021, "sourceType": "script" },
  "rules": { "no-undef": "error" }
}

Adding "env": { "node": true } alone resolved all three errors — measured exit code changed from 1 to 0 with no other change. Note ESLint v9.39.5 still runs eslintrc mode when forced, but prints a deprecation warning that it will be removed in v10.

5. A gotcha we hit while reproducing this: parserOptions matters too

With a totally empty .eslintrc.json (no env, no parserOptions), the same file failed earlier — at parse time, not lint time:

/tmp/eslint-repro/bad.js
  1:1  error  Parsing error: The keyword 'const' is reserved

That happens because legacy config defaults ecmaVersion to a very old ES version (ES5) when nothing else specifies it, and const isn't valid ES5 syntax. This is a different failure from no-undef and is easy to mistake for the same misconfiguration — the fix is a parserOptions.ecmaVersion bump (or switching to flat config, where the default parser target is modern JS and this doesn't come up).

6. Same error, browser globals instead of Node

The identical no-undef mechanism fires for frontend code missing browser globals — 'window' is not defined, 'document' is not defined, 'fetch' is not defined. The fix is the same shape, just globals.browser (flat config) or "env": { "browser": true } (legacy) instead of the Node equivalents. Mixed projects (a Node build script alongside browser-run client code) commonly need per-file-glob config blocks, one targeting each environment, rather than a single global setting.

Frequently asked questions

Why does ESLint flag 'process' or 'require' as undefined when my code runs fine in Node?

ESLint only performs static analysis on the syntax you write — it doesn't execute your code or know which runtime will run it. The no-undef rule flags any identifier that isn't declared locally and isn't in ESLint's configured list of known globals. We measured that an empty config has zero Node globals configured, so process, require and module all fail, even though they work perfectly at runtime under Node.js.

What's the fastest fix for a Node.js project on ESLint v9?

Add languageOptions.globals: globals.node to your eslint.config.js (install the globals npm package, or hand-write the object). We confirmed this changes the exit code from 1 (3 errors) to 0 on the identical file.

I'm still on .eslintrc.json — do I need to migrate to fix this?

No. Add "env": { "node": true } to your .eslintrc.json (or .eslintrc.js/.yml). We measured this resolves the same 3 errors on ESLint v9.39.5 running in legacy mode, without migrating to flat config. Migration will eventually be required since ESLint plans to remove eslintrc support in v10, but it isn't required to fix this specific error today.

Why did I also get a 'Parsing error: the keyword const is reserved' message?

That's a separate issue layered on top: an .eslintrc.json with no parserOptions.ecmaVersion defaults to an old ECMAScript target (ES5) that doesn't support const. We measured this as a distinct parse-time failure, before no-undef even runs. Set parserOptions.ecmaVersion (e.g. 2021) or switch to flat config, which defaults to a modern parser target.

Does the same fix apply to browser globals like window or document?

Yes — it's the same no-undef mechanism. Use globals.browser (flat config) or "env": { "browser": true } (legacy) in place of the Node equivalents. If one config file lints both a Node build script and browser-run client code, use separate config blocks scoped by file glob rather than one global setting for everything.

Written with AI assistance. Measured on 2026-10-03 with ESLint v9.39.5 and Node.js v24.21.0, running npx eslint directly on a CommonJS file, both with flat config (eslint.config.js, the v9 default) and with legacy .eslintrc.json (ESLINT_USE_FLAT_CONFIG=false). Every message, line:column and exit code below is copy-pasted output, not paraphrased. PlainHeaders is our extension; ESLint is free.