microsoft/typescript-go · critical

Refusing to publish 'typescript' with the latest tag from no

Error message

Refusing to publish 'typescript' with the latest tag from non-release version ${version}.

What it means

When publishing as 'typescript', the npm dist-tag is derived from the version: dev maps to next, beta/rc pass through, and 'latest' is allowed only for the exact hardcoded nativePreviewReleaseVersion. Any other version without a dev/beta/rc prerelease would otherwise be published as latest, so the script refuses.

Source

Thrown at Herebyfile.mjs:1240

        version += `-${options.setPrerelease}`;
    }
    else if (match[2]) {
        version += match[2];
    }

    return version;
});

function getPublishTag() {
    if (publishAsTypescript) {
        const version = getVersion();
        if (!version) {
            throw new Error("Publishing as 'typescript' requires a version before selecting an npm tag.");
        }
        const match = version.match(/-(dev|beta|rc)(?:[.-]|$)/);
        if (match?.[1]) return match[1] === "dev" ? "next" : match[1];
        if (version === nativePreviewReleaseVersion) return "latest";
        throw new Error(`Refusing to publish 'typescript' with the latest tag from non-release version ${version}.`);
    }
    return "latest";
}

const extensionDir = path.resolve("./_extension");
const nightlyExtensionDir = path.resolve("./_extension-nightly");
const builtNpm = path.resolve("./built/npm");
const builtVsix = path.resolve("./built/vsix");
const builtPublishedPlatformPackages = path.resolve("./built/published-platform-packages");
const builtSignTmp = path.resolve("./built/sign-tmp");
const publishedTypeScriptAliasPackageName = "@typescript/bundled-typescript";
const releasePackageEnv = { COREPACK_ENABLE_STRICT: "0" };

const getSignTempDir = memoize(async () => {
    const dir = path.resolve(builtSignTmp);
    await rimraf(dir);
    await fs.promises.mkdir(dir, { recursive: true });
    return dir;

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Publish from main with an explicit prerelease: --setPrerelease=beta.1 or dev.YYYYMMDD
  2. For a stable release, set nativePreviewReleaseVersion to that exact version on the native release branch
  3. Log/inspect the computed getVersion() value before running publish tasks

Example fix

# before
hereby publish --forRelease  # version 5.9.2, no prerelease

# after
hereby publish --forRelease --setPrerelease=beta.1  # version 5.9.2-beta.1 -> tag beta
Defensive patterns

Strategy: validation

Validate before calling

function assertPublishTagSafe(version, hardcoded) {
  const m = version.match(/-(dev|beta|rc)(?:[.-]|$)/);
  const isPrerelease = !!m;
  const isHardcodedStable = version === hardcoded;
  if (!isPrerelease && !isHardcodedStable) {
    throw new Error(`Refusing latest tag for ${version}; pass --setPrerelease or hardcode the release version.`);
  }
}

Type guard

/** @param {string} version @returns {boolean} */
function hasPublishablePrerelease(version) {
  return /-(dev|beta|rc)(?:[.-]|$)/.test(version);
}

Try / catch

try { publish(); } catch (e) { if (/Refusing to publish/.test(e.message)) { stop the pipeline; never bypass with --tag latest by hand; } throw e; }

Prevention

When it happens

Trigger: Publishing with a plain X.Y.Z version (no prerelease suffix, no --setPrerelease) while publishAsTypescript is true and the version does not equal nativePreviewReleaseVersion.

Common situations: version.go lost its prerelease suffix on a branch; attempting a stable release without hardcoding nativePreviewReleaseVersion on a release branch; running publish tasks with unexpected getVersion() results.

Related errors


AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16). Data as JSON: /api/errors/a0b3b8343a3556be. Report an issue: GitHub.