LEARN · DEBUGGING GUIDE

Nx Affected Command Not Detecting Changes: Debugging Guide

If `nx affected:test` or `nx affected:build` reports 'No affected projects found' despite clear modifications, your git base SHA or project graph is stale. Here's how to verify and fix it in under 5 minutes.

IntermediateBuild tools7 min read

What this usually means

The `nx affected` command determines which projects are affected by comparing the current file tree against a git base (default: `main` or `master`). If the base is wrong — e.g., a stale SHA, a rebase that rewrote history, or a shallow clone — Nx sees no diff. Alternatively, if the project graph (`project.json`, `nx.json`, or implicit dependencies) is outdated or misconfigured, Nx may not recognize that a file belongs to any project. Another common cause is using `--files` or `--uncommitted` incorrectly, or running `nx affected` in a detached HEAD state where the base doesn't match what you expect.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 1Run `git log --oneline -5` to see recent commits and verify your current HEAD is where you think it is.
  • 2Check the default base: `npx nx show projects --affected --base=main --head=HEAD`. If that works, your `nx.json` `base` setting is likely wrong.
  • 3Run `npx nx affected:graph --base=main --head=HEAD` to visualize which projects are considered affected. Compare with your actual changes.
  • 4Check if your changes are committed: `git status`. `nx affected` by default compares commits, not working directory.
  • 5Inspect `nx.json` for `affected.defaultBase` — if not set, Nx defaults to `main`.
  • 6Verify project graph: `npx nx graph` and click on the file you changed — ensure it's part of a project node.
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • search`nx.json` — look at `affected.defaultBase` and `base` settings.
  • search`project.json` in each project — verify `sourceRoot`, `implicitDependencies`, and `namedInputs`.
  • search`.nxignore` or `.gitignore` — files ignored here are also ignored by Nx.
  • searchCI pipeline config (e.g., `.github/workflows/ci.yml`) — examine how `base` and `head` are derived.
  • search`git reflog` — to see if recent rebases or force pushes changed commit history.
  • search`npx nx graph --file=output.html` — generate the full project graph and look for missing project mappings.
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warning`affected.defaultBase` in `nx.json` points to a branch that doesn't exist locally (e.g., `origin/main` instead of `main`).
  • warningThe git base commit is stale after a rebase or merge — the base SHA no longer exists in the local repo.
  • warningShallow clone in CI — git depth < 2 means Nx can't find the base commit.
  • warningFile changes are uncommitted and `--uncommitted` flag isn't used.
  • warningProject `sourceRoot` is misconfigured — the changed file falls outside any project's `sourceRoot`.
  • warningImplicit dependencies are missing — a generated or config file change isn't mapped to any project.
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildSet `affected.defaultBase` explicitly in `nx.json` to the branch name (e.g., `"main"`).
  • buildUse `--base` and `--head` flags explicitly: `nx affected:test --base=origin/main~1 --head=origin/main`.
  • buildIn CI, fetch the full git history: `git fetch --unshallow` or set `fetch-depth: 0`.
  • buildCommit your changes or use `--files` or `--uncommitted` flag: `nx affected:test --uncommitted`.
  • buildRun `nx reset` to clear the Nx cache and recompute the project graph.
  • buildUpdate `sourceRoot` in each project's `project.json` to match where your source files actually live.
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedRun `npx nx show projects --affected --base=main --head=HEAD` and confirm the expected projects appear.
  • verifiedCheck the graph: `npx nx affected:dep-graph --base=main --head=HEAD` shows exactly which projects are affected.
  • verifiedRun `npx nx affected:test --base=main --head=HEAD` on a small project and see it execute.
  • verifiedModify a file, commit, then run the same command — it should detect the change.
  • verifiedCompare with `git diff --name-only main...HEAD` — every changed file should belong to at least one affected project.
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningDon't assume `main` exists locally — in CI, you often need `origin/main`.
  • warningDon't forget that `nx affected` compares commits, not the working tree — uncommitted changes are ignored unless you use `--uncommitted`.
  • warningDon't rely on `--all` flag thinking it includes all projects; it only changes the default base to the empty tree.
  • warningDon't ignore `nx.json` configuration — check both `affected.defaultBase` and `tasksRunnerOptions`.
  • warningDon't skip `nx reset` when the graph seems wrong — stale cache is a common gotcha.
  • warningDon't use `--base` with a commit SHA that is not an ancestor of `HEAD` — Nx will fall back to an empty diff.
( 07 )War story

CI Pipeline Skips Tests After Rebase

Senior DevOps EngineerNx 15.8, Angular 16, GitHub Actions, pnpm

Timeline

  1. 09:15Developer rebases feature branch onto latest main and force pushes.
  2. 09:20CI triggers on the push; runs `nx affected:test --base=main --head=HEAD`.
  3. 09:21CI outputs 'No affected projects found' — pipeline passes but tests are skipped.
  4. 09:35I'm paged because the PR shows green but changes are clearly in the `shared/ui` library.
  5. 09:40Check the CI logs: `git log --oneline` shows HEAD is at the rebased commit, but `main` is not fetched.
  6. 09:42Run `git branch -a` in CI — `main` branch doesn't exist locally; only `origin/main`.
  7. 09:43Fix: set `--base=origin/main~1` and ensure `fetch-depth: 0` in the checkout step.
  8. 09:50Re-run CI — now detects 3 affected projects and runs tests correctly.

The incident started when a developer rebased their feature branch onto the latest main and force-pushed. The CI pipeline, which uses `nx affected:test --base=main`, ran and reported 'No affected projects found'. The pipeline passed with a green checkmark, but the developer knew they had changed multiple files in the `shared/ui` library. I was paged because the PR looked clean but tests were never executed.

I first checked the git history in the CI runner. `git log --oneline -5` showed the rebased commits, but `git branch -a` revealed that the `main` branch didn't exist locally — only `origin/main` was present. The default `--base=main` was resolving to a non-existent ref, so Nx fell back to an empty diff. The `fetch-depth: 1` in the checkout step meant only the PR head was fetched, not the full history.

I updated the GitHub Actions workflow to set `fetch-depth: 0` and changed the affected command to `--base=origin/main~1 --head=HEAD` to ensure the base commit existed. After rerunning, `nx affected:test` correctly identified three affected projects and ran the tests. The root cause was a combination of shallow clone and missing base branch reference, which is a common pitfall in CI/CD with rebased branches.

Root cause

Shallow clone in CI combined with `--base=main` referencing a non-existent local branch after rebase.

The fix

Set `fetch-depth: 0` in the checkout step and use `--base=origin/main~1` (or `origin/main`) explicitly in the affected command.

The lesson

Always ensure the base branch is available locally in CI and that git history is deep enough to include the base commit. Use explicit `--base` and `--head` flags to avoid ambiguity.

( 08 )How Nx Computes Affected Projects

Nx uses the project graph to map files to projects. When you run `nx affected:test --base=main`, Nx does the following: It runs `git diff --name-only main...HEAD` to get the list of changed files. Then it looks up each file in the project graph to find which project(s) own it. Finally, it includes all transitive dependencies of those projects. If any step fails (e.g., no diff, or file not mapped), you get 'No affected projects'.

The project graph is built from `project.json` files, `nx.json`, and workspace conventions. Each project has a `sourceRoot` that defines where its source files live. If a changed file falls outside any `sourceRoot`, Nx won't map it to a project. Additionally, implicit dependencies (like `package.json` or global configs) can be defined in `nx.json` under `implicitDependencies`.

( 09 )The Git Base Problem

The most common reason `nx affected` fails is a mismatched git base. Nx's default base is `main` (or `master`). In CI, the base branch is often not fetched, or it's a different ref like `origin/main`. After a rebase, the base commit may no longer be an ancestor of HEAD, causing an empty diff. The fix is to always use explicit `--base` and `--head` flags that refer to existing refs, and to ensure full git history is available.

You can test this locally: run `git diff --name-only main...HEAD`. If that returns nothing, `nx affected` will also see nothing. Use `git merge-base main HEAD` to check if main is an ancestor. If not, you need to rebase or choose a different base.

( 10 )Stale Project Graph Cache

Nx caches the project graph to speed up commands. If you change `project.json`, add a new project, or modify `nx.json`, the cache may become stale. This is especially common when switching branches. The symptom is that `nx graph` shows the correct state but `nx affected` uses the old graph. The fix is `nx reset`, which clears the cache. You can also run `nx affect:graph --verbose` to see if it's using a cached graph.

To prevent this, ensure that your CI pipeline runs `nx reset` before the first affected command, or rely on Nx's automatic cache invalidation (which works in most cases but can fail if the file timestamps are not updated).

( 11 )Uncommitted Changes and the `--uncommitted` Flag

By default, `nx affected` compares two commits. If you haven't committed your changes, they won't be detected. You can use `--uncommitted` to include uncommitted changes, or use `--files` to specify specific files. However, `--uncommitted` compares the working tree to the index, which can be confusing if you have staged and unstaged changes. Best practice is to commit before running affected commands, or use `--files` for ad-hoc checks.

In CI, you always want to compare commits, never uncommitted changes, because the working tree is clean after checkout. So `--uncommitted` is rarely needed in CI.

Frequently asked questions

Why does `nx affected` work locally but not in CI?

The most common reason is that the git base branch (e.g., `main`) doesn't exist in the CI environment. In CI, you typically have a detached HEAD or only the PR branch. Use `--base=origin/main` and ensure you fetch the base branch: `git fetch origin main:main`. Also check that your CI checkout action uses `fetch-depth: 0` to get full history.

What does `nx affected --all` do?

`--all` sets the base to the empty tree, meaning all projects are considered affected. It's useful for running all tests on `main` but not for detecting changes. Do not use `--all` if you want to only run on changed projects — it will run everything.

Can `nx affected` detect changes in generated files?

Yes, if the generated files are committed and tracked by git. However, if the files are in `.gitignore` or `.nxignore`, they are ignored. Also, the file must be within a project's `sourceRoot` or be explicitly listed as an implicit dependency. Generated files outside any project will not trigger affected projects.

How do I debug which files Nx considers changed?

Run `npx nx affected:graph --base=main --head=HEAD --verbose` to see the file diff and how projects are affected. You can also run `git diff --name-only main...HEAD` to compare. If the git diff shows files but `nx affected` doesn't, the file-to-project mapping is broken — check `sourceRoot` in `project.json`.

What if I rebase my branch and `nx affected` stops working?

After a rebase, the base commit (e.g., `main`) may no longer be an ancestor of HEAD. Re-run `git fetch origin main:main` to update the local main, or use `--base=HEAD~1` to compare with the previous commit. Alternatively, use the `--base` flag with the commit SHA of the original base before rebase.