Fixing Nx 'Affected' Detecting All Projects as Changed
nx affected reports every project as affected on a trivial change, so you lose the speed-up entirely. This page explains why Nx widens the affected set and how to narrow it back to the real change.
Exact symptoms and error messages
A one-line change to a single library marks the whole graph affected:
> nx affected:build --base=origin/main
Note: Nx affected calculated 37 projects to run...
(expected 2)
Root cause analysis
Nx marks everything affected when a change touches a global input — the root package.json, the lockfile, nx.json, or a shared tsconfig.base.json — because those legitimately influence every project. It also widens the set when the base ref is unreachable (a shallow clone) or when namedInputs are configured so that unrelated files feed a project's hash. The mechanics are the same graph model described in Nx Workspace Architecture.
Nx computes affected by hashing a set of named inputs per project and diffing against the base. Anything included in an input that many projects share — a root config, a workspace-wide glob — becomes a shared input, so touching it invalidates every project that lists it. That is correct when the file genuinely affects all projects, and wasteful when an input set is broader than it needs to be. Tuning namedInputs is really about telling Nx precisely which files change a project's output.
The base ref is the other half. Nx diffs your working tree against --base, and if that commit is missing from a shallow CI clone, it cannot compute a diff and conservatively marks everything affected. So an all-projects result on a trivial change is almost always one of two things: an input set that folds in unrelated files, or a base that does not resolve — and the affected graph tells you which.
Resolution and configuration patch
Scope namedInputs so formatting and docs changes do not invalidate builds, and ensure the base is reachable:
// nx.json
{
"namedInputs": {
"default": ["{projectRoot}/**/*", "sharedGlobals"],
"production": ["default", "!{projectRoot}/**/*.spec.ts", "!{projectRoot}/**/*.md"],
"sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"]
}
}
Separate a lean production input from the default so that test-only and docs changes do not invalidate build and deploy targets:
// nx.json
{
"targetDefaults": {
"build": { "inputs": ["production", "^production"] }
},
"namedInputs": {
"default": ["{projectRoot}/**/*", "sharedGlobals"],
"production": ["default", "!{projectRoot}/**/*.spec.ts", "!{projectRoot}/**/*.md", "!{projectRoot}/**/*.stories.ts"],
"sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"]
}
}
Now a change to a spec file re-runs tests but not builds, and sharedGlobals is kept deliberately small so only a genuinely global file invalidates the whole graph.
CLI validation and debug commands
# Show exactly why a project is affected
nx affected:graph --base=origin/main
# Ensure the base commit is present
git fetch origin main --depth=100
# List the affected projects only
nx show projects --affected --base=origin/main
Prevention and CI guardrails
- Fetch enough history that
--baseresolves to a real commit. - Exclude spec, markdown, and config-only files from the
productioninput set. - Keep
sharedGlobalsminimal — every file listed there invalidates the whole graph. - Review
nx affected:graphwhen the affected set looks too large.
Diagnosing affected with the project graph
When the affected set is wrong, guessing is slow; Nx can show you exactly why a project was included. The affected graph renders the change and the edges that propagated it, so you can see whether a project was pulled in by a real dependency or by a shared input.
# Visualize what the change affected and why
nx affected:graph --base=origin/main
If a project is highlighted with no path back to your change, the culprit is a shared input, not a dependency edge — trim the input set. If it is highlighted through a chain of dependencies you did not expect, you have found real coupling worth knowing about. Either way, the graph turns 'Nx ran too much' from a mystery into a specific, fixable cause.
Keeping sharedGlobals honest
sharedGlobals is the most dangerous input to get wrong, because every file listed there invalidates the entire workspace. It is easy to add a root file 'just in case' and quietly convert every future change to that file into a full rebuild. Treat the list as a deliberate, reviewed set: the lockfile, the base tsconfig, and little else.
Audit it whenever affected starts running more than it should. A .env file, a formatting config, or a docs generator config sitting in sharedGlobals will force a full run on every change to it — usually not what you want. The discipline is to ask, for each entry, 'does changing this genuinely change every project's output?' If the honest answer is no, it does not belong in the global set, and moving it out restores affected's ability to keep changes small.
Frequently Asked Questions
Is it ever correct for Nx to mark everything affected?
Yes — a change to the lockfile, root tsconfig, or nx.json genuinely can change every project's output, so a full run is the safe result. The goal is to stop unrelated files from triggering it.
Why does the base branch matter so much?
Nx diffs your working tree against the base commit. If a shallow clone means that commit is absent, Nx cannot compute a diff and conservatively treats everything as changed.
How do I see why Nx marked a project affected?
Run nx affected:graph --base=<ref>. It renders the change and the edges that propagated it, so you can tell whether a project was pulled in by a real dependency or by a shared input — and fix the right thing.
What belongs in sharedGlobals?
Only files that genuinely change every project's output — typically the lockfile and the base tsconfig. Anything else there forces a full rebuild on every change to it, which is almost never intended.
Why do spec-file changes trigger builds?
Because the build target's inputs include the spec files. Define a lean production input that excludes *.spec.ts, *.md, and stories, and point build/deploy targets at it so only production sources invalidate them.
Related
- CI/CD Pipeline Optimization for Monorepos — the overview for fast monorepo pipelines.