nodejs/node · error · Error

Cannot implicitly apply the "latest" tag because previously

Error message

Cannot implicitly apply the "latest" tag because previously published version ${highestVersion} is higher than the new version ${manifest.version}. You must specify a tag using --tag.

What it means

Thrown by `npm publish` when the new version is lower than or equal to the highest version already published (`semver.gte(highestVersion, manifest.version)`), the `tag` config is at its default (`latest`), and `--force` is not set. npm will not silently demote `latest` to an older release.

Source

Thrown at deps/npm/lib/commands/publish.js:180

      const msg = `This command requires you to be logged in to ${outputRegistry}`
      if (dryRun) {
        log.warn(this.#command, `${msg} (dry-run)`)
      } else {
        throw Object.assign(new Error(msg), { code: 'ENEEDAUTH' })
      }
    }

    if (!force) {
      const { highestVersion, versions } = await this.#registryVersions(resolved, registry)
      /* eslint-disable-next-line max-len */
      const highestVersionIsGreater = !!highestVersion && semver.gte(highestVersion, manifest.version)

      if (versions.includes(manifest.version)) {
        throw new Error(`You cannot publish over the previously published versions: ${manifest.version}.`)
      }

      if (highestVersionIsGreater && isDefaultTag) {
        throw new Error(`Cannot implicitly apply the "latest" tag because previously published version ${highestVersion} is higher than the new version ${manifest.version}. You must specify a tag using --tag.`)
      }
    }

    const access = opts.access === null ? 'default' : opts.access
    const verb = this.isStage ? 'Staging' : 'Publishing'
    let msg = `${verb} to ${outputRegistry} with tag ${defaultTag} and ${access} access`
    if (dryRun) {
      msg = `${msg} (dry-run)`
    }

    log.notice('', msg)

    let stageId
    if (!dryRun) {
      if (this.isStage) {
        // Stage intentionally bypasses otplease — 2FA is deferred to approve/reject
        const res = await libpub(manifest, tarballData, {
          ...opts,

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Pass an explicit `--tag` (e.g. `--tag legacy` or `--tag lts`) so the older version gets its own dist-tag instead of `latest`.
  2. Set `"publishConfig": { "tag": "..." }` in package.json.
  3. Publish a version strictly greater than the current highest version.
  4. Use `--force` only if you knowingly want to move `latest` to an older version.

Example fix

// before
npm publish   // publishing 1.5.1 when 2.0.0 is latest
// after
npm publish --tag backport
Defensive patterns

Strategy: validation

Validate before calling

const semver = require('semver')
function chooseTag(newVersion, highestPublished, cliTag, publishConfigTag) {
  const tag = cliTag || publishConfigTag || 'latest'
  if (highestPublished && semver.gte(highestPublished, newVersion) && tag === 'latest') {
    return 'backport' // or 'legacy', 'lts' — anything non-latest
  }
  return tag
}

Prevention

When it happens

Trigger: Publishing a version where `highestVersion` from the registry `>=` `manifest.version`, while `isDefaultTag` is true (no `--tag`, no `publishConfig.tag`), without `--force`.

Common situations: Publishing a patch to an old release line (e.g. `1.2.x`) after `2.0.0` is already `latest`; downgrading; republishing after a hotfix branch merge into a lower minor.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/d5c385c067ad93a9. Report an issue: GitHub.