Skip to content
Latchkey

npm version "Git working directory not clean" - Fix Release Tagging in CI

By default npm version bumps package.json, makes a git commit, and creates a version tag. In CI it fails when the working tree is dirty, there is no git identity configured, or the tag already exists - because the git step, not the version bump, cannot complete.

What this error means

npm version <patch|minor|...> aborts with Git working directory not clean, a missing-identity error, or a tag-already-exists failure. The version may bump but the commit/tag step fails, leaving the release half-done.

npm output
npm error Git working directory not clean.
npm error A   dist/bundle.js
# or
npm error tag 'v1.4.0' already exists
# or
*** Please tell me who you are. (git config user.email)

Diagnose it: reproduce the CI install locally

Install failures are usually environment drift rather than a broken lockfile: a different package-manager major, a different Node version, or a cache that is being restored from a run with different inputs. Reproduce the CI conditions before changing the lockfile, because regenerating it hides the real cause.

Terminal
# match the runner exactly, then install from a clean slate
node --version && npm --version
rm -rf node_modules
npm ci --foreground-scripts

# if that succeeds locally but fails in CI, the difference is the cache
# or the package-manager version, not your lockfile

Common causes

Uncommitted changes in the working tree

npm version refuses to commit when the tree is dirty (e.g. build artifacts present). It wants a clean tree to make a precise version commit.

No git identity or the tag already exists

CI runners often lack user.name/user.email, so the commit fails; or a re-run tries to create a tag that already exists.

How to fix it

Clean the tree and configure git, or skip git steps

Ensure a clean tree and a git identity, or disable npm version’s git actions if CI tags separately.

Terminal
git config user.email "ci@example.com"
git config user.name "CI"
git status --porcelain        # must be empty
npm version patch
# or bump without committing/tagging:
npm version patch --no-git-tag-version

Make the release step idempotent

  1. Run version bumping before producing build artifacts so the tree is clean.
  2. Set a git identity in the CI job.
  3. Guard against re-tagging an existing version on re-runs.

Verify the fix survives a cold cache

A green run immediately after a fix often proves nothing, because it restored a cache written before the change. Force a cold install once to confirm the fix is real.

.github/workflows/ci.yml
# temporarily bust the cache key to prove the fix on a cold runner
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: npm
    cache-dependency-path: package-lock.json
# then bump this suffix once, run, and remove it
#   key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}-v2

How to prevent it

  • Bump versions on a clean working tree.
  • Configure git identity in CI before npm version.
  • Use --no-git-tag-version when CI handles tags separately.

Frequently asked questions

What causes npm version "Git working directory not clean"?
There are 2 common causes: uncommitted changes in the working tree and no git identity or the tag already exists. npm version refuses to commit when the tree is dirty (e.g.
How do I fix npm version "Git working directory not clean"?
There are 2 fixes depending on which cause you have: clean the tree and configure git, or skip git steps and make the release step idempotent. Work through them in order, since the first is the most common.
What does npm version "Git working directory not clean" actually mean?
npm version <patch|minor|...> aborts with Git working directory not clean, a missing-identity error, or a tag-already-exists failure.
How do I stop npm version "Git working directory not clean" happening again?
Bump versions on a clean working tree. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card