vercel/turborepo · error · Error
Invalid version: ${version}
Error message
Invalid version: ${version} What it means
The update-versioned-schema-json codemod rewrites turbo.json `$schema` URLs to versioned ones (https://<major-minor-patch>.turborepo.dev/schema.json). getVersionedSchemaUrl first tries semver.parse (preserving prerelease) then semver.coerce (lenient); when both return null the input contains no extractable version and it throws `Invalid version: <version>`.
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 9f94a7d215)
Solutions
- Set devDependencies.turbo to a concrete published version before running the codemod (e.g. `^2.3.0`), then retry
- If you must keep `workspace:*` day-to-day, temporarily pin a real version, run the codemod, then restore your alias — the schema URL it writes stays valid
- If invoking the transform programmatically, pass a parseable version string (semver.parse it first) instead of a range/alias
Example fix
// package.json (before)
"devDependencies": { "turbo": "workspace:*" }
// after
"devDependencies": { "turbo": "^2.3.0" } Defensive patterns
Strategy: type-guard
Validate before calling
import { parse, coerce } from 'semver';
function isResolvableVersion(v: string | undefined): boolean {
return Boolean(v) && Boolean(parse(v!) ?? coerce(v));
}
// before running the codemod: read devDependencies.turbo and require isResolvableVersion Type guard
import { parse, coerce } from 'semver';
function isSemverResolvable(version: string): boolean {
return Boolean(parse(version) ?? coerce(version));
} Try / catch
try {
getVersionedSchemaUrl(turboVersion);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Invalid version:')) {
// fall back to the concrete version from the lockfile/registry instead of an alias
return getVersionedSchemaUrl(await resolveInstalledTurboVersion());
}
throw err;
} Prevention
- Pin turbo to concrete semver versions (`^2.3.0`), not aliases (`workspace:*`, `latest`, `file:...`, `github:...`)
- If you must alias locally, temporarily pin a real version around codemod runs
- Guard any wrapper script with semver.parse/coerce before passing versions in
When it happens
Trigger: A version string with no semver-shaped digits at all: `workspace:*`, `latest`, `file:../turbo`, `github:vercel/turbo`, `link:...`, or an empty string — anything semver.coerce cannot find numbers in.
Common situations: Monorepos aliasing turbo to a local workspace build (`workspace:*`, common in pnpm/Yarn setups); tarball or git dependencies for turbo; overridden resolutions replacing the real version.
Related errors
- Unable to fetch the latest version of ${packageName}
- turbo@${to} does not exist
- Unable to read package.json
- Unable to write package.json
- New workspace root detected - unexpected 'workspaces' field
AI-assisted analysis of vercel/turborepo@9f94a7d215 (2026-08-16).
Data as JSON: /api/errors/e56dadd4700ede9b.
Report an issue: GitHub.