pnpm/pnpm · error · PnpmError

VERSIONING_INVALID_LANE_NAME

VERSIONING_INVALID_LANE_NAME

Error message

Invalid lane name: ${laneName}. "main" is the reserved default lane; spell it in lowercase to move packages back onto it.

What it means

Thrown by `pnpm lane <name>` when the name is a case variant of the reserved default lane — `Main`, `MAIN`, etc. — rather than the exact lowercase `main`. `main` is not a stored lane; it means "move back onto the stable release track", so a near-identical casing would create a real lane that shadows the reserved one.

Source

Thrown at pnpm11/releasing/commands/src/lane/index.ts:110

  for (const [key, lane] of Object.entries(lanes)) {
    for (const dir of refs.refToDirs(key)) {
      laneByDir.set(dir, { key, lane })
    }
  }

  let output: string
  if (laneName === MAIN_LANE) {
    for (const project of selected) {
      const existing = laneByDir.get(project.dir)
      if (existing != null) {
        delete lanes[existing.key]
      }
    }
    output = `Moved to the main lane:\n${selected.map((project) => `  ${refFor(project, refs)}\n`).join('')}` +
      'The accumulated stable versions release on the next "pnpm version -r" run.'
  } else {
    if (laneName.toLowerCase() === MAIN_LANE) {
      throw new PnpmError('VERSIONING_INVALID_LANE_NAME', `Invalid lane name: ${laneName}. "main" is the reserved default lane; spell it in lowercase to move packages back onto it.`)
    }
    // A purely numeric lane name is rejected because semver parses an
    // all-digit prerelease identifier as a number, which changes sorting
    // semantics.
    if (!/^[0-9A-Z-]+$/i.test(laneName) || /^\d+$/.test(laneName)) {
      throw new PnpmError('VERSIONING_INVALID_LANE_NAME', `Invalid lane name: ${laneName}. Lane names may contain only alphanumerics and hyphens, and cannot be purely numeric.`)
    }
    for (const project of selected) {
      const existing = laneByDir.get(project.dir)
      if (existing != null && existing.lane !== laneName) {
        throw new PnpmError('VERSIONING_ALREADY_ON_LANE', `${refFor(project, refs)} is already on the "${existing.lane}" lane. Move it back with "pnpm lane main" first.`)
      }
      if (existing == null) {
        lanes[refFor(project, refs)] = laneName
      }
    }
    output = `Moved to the "${laneName}" lane:\n${selected.map((project) => `  ${refFor(project, refs)}\n`).join('')}`
  }

View on GitHub (pinned to 6261b7f388)

Solutions

  1. Spell it lowercase: `pnpm lane main --filter <pkg>...`
  2. For a genuine separate lane, pick a name that is not a case variant of `main`

Example fix

# before
pnpm lane Main --filter @acme/napi...
# after
pnpm lane main --filter @acme/napi...
Defensive patterns

Strategy: type-guard

Type guard

const MAIN_LANE = 'main'
const isAcceptableLaneName = (name) =>
  name === MAIN_LANE || name.toLowerCase() !== MAIN_LANE

Prevention

When it happens

Trigger: Any invocation where `laneName.toLowerCase() === 'main'` but `laneName !== 'main'`: `pnpm lane Main --filter <pkg>...`, `pnpm lane MAIN ...`.

Common situations: Capitalizing commands out of habit or autocorrect; scripting with a variable that got title-cased.

Related errors


AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17). Data as JSON: /api/errors/d70a4a35b15dc236. Report an issue: GitHub.