vercel/turborepo · error · Error

Invalid version

Error message

Invalid version: ${version}

What it means

During migration, the update-versioned-schema-json transform builds the new schema.json URL from the resolved turbo version. It parses the version with semver.parse to keep prerelease parts, and falls back to semver.coerce for loose strings. It throws this Error only when both fail, which means the version is not recognizable as semver at all.

Solutions

  1. Check the version you passed with --to and correct it to a semver string such as 2.9.4 or 2.9.4-canary.5
  2. Inspect your package.json for a malformed turbo version and fix it to a real published version
  3. Re-run the codemod after correcting the source of the version string

Example fix

// before
 getVersionedSchemaUrl("main");
// after
getVersionedSchemaUrl("2.9.4-canary.5"); // "https://2-9-4-canary-5.turborepo.dev/schema.json"
Defensive patterns

Strategy: type-guard

Validate before calling

import semver from "semver";

// accept only versions that parse or coerce before generating schema URLs
function assertResolvableVersion(version: string): void {
  if (!semver.parse(version) && !semver.coerce(version)) {
    throw new Error(`Invalid version: ${version}`);
  }
}

Type guard

import semver from "semver";

function isResolvableSemver(version: string): boolean {
  return semver.parse(version) !== null || semver.coerce(version) !== null;
}

Prevention

When it happens

Trigger: getVersionedSchemaUrl receives a string with no leading numeric version, such as "banana", "v", or "". semver.parse returns null and semver.coerce also returns null, so resolved is undefined and it throws.

Common situations: A package.json or lockfile contains a corrupted turbo version. A script passes a branch name or arbitrary string where a version belongs. An empty version string after a failed earlier step.

Related errors


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/e56dadd4700ede9b. Report an issue: GitHub.

Appendix: source

Thrown at packages/turbo-codemod/src/transforms/update-versioned-schema-json.ts:104

 * Replaces dots with hyphens to create a valid DNS subdomain.
 * e.g., "2.7.5" -> "v2-7-5", "2.9.4-canary.5" -> "v2-9-4-canary-5"
 */
function versionToSubdomain(version: string): string {
  return `v${version.replaceAll(".", "-")}`;
}

/**
 * Generates the new versioned schema URL.
 * Uses semver.parse to preserve prerelease identifiers (e.g., canary.5),
 * falling back to semver.coerce for non-standard version strings.
 */
export function getVersionedSchemaUrl(version: string): string {
  // parse preserves prerelease: "2.9.4-canary.5" -> "2.9.4-canary.5"
  // coerce strips it: "2.9.4-canary.5" -> "2.9.4"
  const parsed = parse(version);
  const resolved = parsed ? parsed.version : coerce(version)?.version;
  if (!resolved) {
    throw new Error(`Invalid version: ${version}`);
  }
  const subdomain = versionToSubdomain(resolved);
  return `https://${subdomain}.turborepo.dev/schema.json`;
}

/**
 * Updates any old schema URLs in file content to the new versioned URL
 */
function updateSchemaUrls(content: string, newUrl: string): string {
  let updated = content;
  // Replace static old URLs
  for (const oldUrl of OLD_SCHEMA_URLS) {
    updated = updated.replaceAll(oldUrl, newUrl);
  }
  // Replace outdated versioned URLs (e.g., v2-7-4 -> v2-7-5)
  updated = updated.replaceAll(VERSIONED_SCHEMA_URL_REGEX, newUrl);
  return updated;
}

View on GitHub (pinned to f9245100cf)