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 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.
# 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 lockfileCommon 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.
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-versionMake the release step idempotent
- Run version bumping before producing build artifacts so the tree is clean.
- Set a git identity in the CI job.
- 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.
# 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') }}-v2How 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.