A release is several steps, not one
A release looks like one action but is a sequence of side effects: decide the version, create a tag, record release notes, publish a package or build, and often deploy. Each step can succeed or fail on its own, and a failure in a later step leaves the earlier ones done. The classic result is a tag that exists for a version whose package was never published, or a package that exists with no matching tag or notes. Nobody designed that state, so nobody wrote down how to leave it.
What the rules allow on a retry
Registries are strict. For npm, publishing a name and version that already exists fails, and a name and version pair that has been published can never be used again, even if the package is later removed. Semantic Versioning says the same thing as a rule for people: the contents of a released version must not change, and any change is a new version. So after a half-failed release you have exactly two clean options. If the version was never published, resume at the failed step and publish that version. If it was published, even partly, leave it alone and release a new version.
- Version never published: resume at the failed step.
- Version published, even partly: leave it and release a new version.
Read the state before you act
Before touching anything, build a small table of facts from read-only checks: does the tag exist and what commit does it point to; does the registry list that version; do release notes exist; did a deploy step run. Compare the rows to decide which step failed. Most mistakes at this point come from re-running the whole pipeline, which tries to create the tag again, publish again and fail at the first step that was already done, or worse, succeeds at a step it should have skipped.
If the matrix is wider than the box, scroll horizontally to read every column. Keyboard: focus the matrix and use Left/Right.
step | state seen | action on retry
tag | exists, points at release | skip
release notes | missing | create
package | version not in the registry | publish now
deploy | not started | run after the packageMaking a re-run safe
Write each step to check before it acts. Skip tagging if the tag already points at the right commit. Skip publishing if the version is already in the registry, and say so in the log instead of failing silently. Keep tagging and publishing as separate steps so a failure in one does not hide the state of the other, and run the whole sequence in dry-run mode on a scratch branch first; the npm publish command has a dry-run option that reports what it would do without changing the registry. What never to do: delete and recreate a tag after other people have fetched it, force a publish, unpublish to retry, or renumber a released version to hide the gap.
How the paid outcome is accepted
The fixed release-automation job includes a repeat-run test: after one approved run from the release branch creates one tag and one release record, a second run with no new changes must create nothing, and nothing can be released from other branches. Publishing to a registry is excluded unless agreed in writing, because it needs your credentials. If you want the release path checked every month, the monthly rehearsal covers a release dry run on a scratch branch. The release job starts at £595, untested, and is paid after you sign off.
Sources and limits
- npm publish documentation Checked 2026-10-11.
- Publishing a name and version that already exists in the registry fails, and a published pair can never be used again, even after unpublishing; a dry-run option reports what would happen without changing anything.
- Semantic Versioning 2.0.0 Checked 2026-10-11.
- Once a version is released its contents must not be modified; any change must be released as a new version.