Back to monorepo orchestration Target affected workspaces Configure turbo pipelines Speed up type-checking

Fixing Nx 'Failed to Process Project Graph'

Every Nx command starts by building the project graph: discovering projects, running plugins that infer targets, and analysing dependencies between projects. When any part of that fails, Nx stops before running a single task and prints Failed to process project graph. The message is generic because the causes are varied — a plugin crash, a malformed configuration file, a stale daemon, a version mismatch between Nx packages, or a file the source analyser cannot parse. This guide shows how to get the real error out of Nx, and the fix for each common cause.

Exact symptoms and error messages

The top-level message, often followed by a stack trace from a plugin:

 NX   Failed to process project graph.

An error occurred while processing files for the @nx/vite/plugin plugin.
  - apps/web/vite.config.ts: Error: Cannot find module '@vitejs/plugin-react'
    Require stack:
    - /repo/apps/web/vite.config.ts

Variants name the daemon or a specific phase:

 NX   Daemon process terminated and closed the connection

Please rerun the command, which will restart the daemon.
If you get this error again, check for any errors in the daemon process logs found in: /repo/.nx/workspace-data/d/daemon.log
 NX   Failed to process project graph. Run "nx reset" to fix this. Please report the issue if you keep seeing it.
 NX   The following projects are defined in multiple locations:
  - @acme/ui:
    - packages/ui
    - packages/ui-legacy

Root cause analysis

Project graph construction runs in stages, and each stage can fail independently. Knowing the stages tells you where to look. The overall architecture is covered in Nx Workspace Architecture.

How Nx builds the project graph Nx discovers project files, runs plugins to infer projects and targets, analyses source files for dependencies, and caches the result in the daemon or workspace data. discover package.json and project.json files plugins infer vite, eslint, jest configs loaded analyse imports source files parsed for edges cache graph daemon + .nx/workspace-data
A failure in any stage aborts the command; the error text usually names the stage and the file.

The common causes, roughly in order of frequency:

  1. A plugin fails while loading a config file. Inference plugins such as @nx/vite/plugin or @nx/jest/plugin actually load vite.config.ts or jest.config.ts to read their settings. If that file imports a package that is not installed, uses an environment variable that is unset, or throws at load time, the plugin fails and so does the graph.
  2. Stale cached state. The daemon or .nx/workspace-data holds a graph from before a branch switch, a dependency upgrade or a moved project. Nx usually detects this, but not always.
  3. Mismatched Nx package versions. nx at one version and @nx/vite or @nx/js at another — often after upgrading only some packages — causes plugin API mismatches.
  4. Duplicate or malformed project definitions. Two package.json files with the same name, invalid JSON in a project.json, or a project folder matched twice.
  5. Source files the analyser cannot handle — rarely, a syntax Nx's parser does not support in a file it scans for imports.

Getting the real error

The first line is rarely enough, and guessing at the cause wastes time because several unrelated problems share the same headline. Turn off the daemon and turn on verbose logging to see the underlying exception:

NX_DAEMON=false NX_VERBOSE_LOGGING=true pnpm nx show projects

Running without the daemon puts graph construction in your terminal's process, so stack traces print directly instead of going to .nx/workspace-data/d/daemon.log. nx show projects is a cheap command that only needs the graph, which makes it a good reproduction.

Narrowing down a project graph failure A sequence of checks from reading the verbose error, to resetting cached state, to aligning Nx versions, to isolating plugins and duplicate projects. Does the verbose error name a config file? Fix that file's load error missing import, env var, syntax yes Does nx reset make it go away? Stale cache or daemon reset after branch switches and upgrades yes no Are @nx/* versions mismatched? Align with nx migrate all nx packages on one version yes no Isolate plugins and duplicates disable plugins one by one; check names no
Get the verbose error first; most causes are identifiable from the file it names.

Resolution

Fix the failing config file

If the error names apps/web/vite.config.ts, load that file directly to reproduce outside Nx:

cd apps/web && pnpm exec vite --version && node --import tsx -e "import('./vite.config.ts').then(() => console.log('config loads'))"

Typical fixes: install the missing plugin in that project's devDependencies (under pnpm's strict layout, a plugin installed only in another package is not resolvable here), guard environment variable access with defaults, or move side effects out of the config file. Config files should be cheap and side-effect-free, because Nx loads them for every graph computation.

Reset cached state

pnpm nx reset

This stops the daemon and deletes the local cache and workspace data. It is safe — the next command recomputes everything — and it fixes stale-state failures after branch switches, git clean operations or dependency upgrades.

Align Nx versions

pnpm list nx @nx/js @nx/vite @nx/eslint --depth 0
pnpm nx migrate latest        # writes package.json updates and migrations.json
pnpm install
pnpm nx migrate --run-migrations

nx migrate updates every @nx/* package together and runs code migrations for configuration changes between versions. Never upgrade nx and its plugins independently.

Resolve duplicate projects

Two projects with the same name cannot coexist. Rename one package, or exclude the legacy folder from the workspace globs and from Nx discovery with .nxignore:

# .nxignore
packages/ui-legacy
examples/**

Isolate a misbehaving plugin

If the error is unclear, temporarily remove plugins from the plugins array in nx.json one at a time and rerun nx show projects. The plugin whose removal fixes the graph is the one to investigate; its include/exclude options can scope it away from folders it should not process.

Causes, signals and fixes Maps each common cause of project graph failure to the signal in the error output and the fix. signal fix config load error stack trace names a config file fix imports or env in that file stale state fails after branch switch; reset fixes nx reset version mismatch plugin API errors, 'is not a function' nx migrate to one version duplicate project 'defined in multiple locations' rename or .nxignore unparseable source parser error on a source file exclude or update Nx
The verbose error almost always contains enough to pick the right row.

Performance problems that look like failures

Some graph "failures" are really timeouts. Inference plugins load every matching config file on each graph computation, and in large repositories that can take long enough for the daemon connection or a CI step timeout to give up. Signals are messages about the daemon closing the connection with no underlying exception in daemon.log, or graph computation that takes tens of seconds on a cold start.

Three measures help. Scope plugins with include and exclude in nx.json, so @nx/vite/plugin only processes folders that are actually Vite projects rather than every file matching **/vite.config.*, including fixtures and examples. Keep config files light: a vite.config.ts that imports a large framework or reads the file system at the top level is slow to load for every graph computation. And make sure the plugin result cache works — Nx caches plugin output keyed by the config file contents, so a config file that embeds a timestamp or generated value defeats it and forces a reload every time.

To measure, run NX_PERF_LOGGING=true pnpm nx show projects and look at the time spent per plugin. A plugin that dominates the total is the one to scope or simplify.

After upgrading Nx

Many graph failures appear the first time a command runs after an Nx upgrade, because configuration formats move between versions. nx migrate --run-migrations applies the code changes that the Nx team ships for each release — renamed options, moved plugins, new defaults — and skipping it leaves the workspace half-upgraded. If a failure appears after an upgrade that was done by editing package.json directly, revert to the previous versions, then upgrade again through nx migrate, commit migrations.json together with the result, and delete it once applied.

Worked example: a CI-only graph failure

A pipeline fails at the first nx affected call with Failed to process project graph, while every developer's machine works. With NX_DAEMON=false NX_VERBOSE_LOGGING=true, the CI log shows the @nx/vite/plugin failing on apps/docs/vite.config.ts: Error: Missing required environment variable DOCS_API_KEY. The config file threw at load time when the variable was unset — developers had it in their shell. The fix moves the check into a Vite plugin hook that runs only during build, so loading the config for inference no longer needs the secret. The team adds a CI step that runs nx show projects with an empty environment to catch similar mistakes early.

Prevention and CI/CD guardrails

  • Keep tool config files side-effect-free; read environment variables lazily inside hooks.
  • Upgrade Nx only with nx migrate, and keep all @nx/* packages on one version.
  • Run nx show projects early in CI as a cheap graph health check with clear logs.
  • Use .nxignore for folders that contain package manifests but are not projects, such as examples and fixtures.

Frequently Asked Questions

Is it safe to run nx reset regularly? Yes. It only deletes caches and stops the daemon. The cost is that the next run recomputes the graph and rebuilds anything not in a remote cache.

Why does the error only happen with the daemon on? The daemon can hold state from an earlier run, and its errors go to a log file rather than your terminal. Running with NX_DAEMON=false usually shows the real error, and nx reset clears stale daemon state.

Can a single broken project stop Nx for everyone? Yes, because the graph is computed for the whole workspace. That is why config files and plugin scopes deserve care in shared repositories.

Where are the daemon logs? In .nx/workspace-data/d/daemon.log inside the repository. Read it when the terminal only shows that the daemon closed the connection; the underlying exception is usually there.

Related

Nx Workspace Architecture