moeru-ai/airi · error

Prerelease sequence must be non-negative: ${sequence}

Error message

Prerelease sequence must be non-negative: ${sequence}

What it means

encodeNumericVersion rejects a negative prerelease sequence. Because the sequence is parsed from the prerelease string by parsePrerelease, a negative value indicates the parser produced one (e.g. it interpreted a malformed identifier as a negative number, or a code path defaulted to -1).

Source

Thrown at integrations/vscode/vscode-airi/scripts/shared.ts:71

  const minor = Number.parseInt(match.groups.minor, 10)
  const patch = Number.parseInt(match.groups.patch, 10)
  const prerelease = match.groups.pre

  const multiplier = 10_000
  const stageBuckets = {
    alpha: 1_000,
    beta: 2_000,
    rc: 3_000,
    stable: 9_000,
  } as const

  const { stage, sequence } = parsePrerelease(prerelease)
  const maxSequence = (multiplier - 1) - stageBuckets[stage]
  if (sequence > maxSequence) {
    throw new Error(`Prerelease sequence overflow for ${stage}: ${sequence} exceeds limit ${maxSequence}`)
  }
  if (sequence < 0) {
    throw new Error(`Prerelease sequence must be non-negative: ${sequence}`)
  }

  const encodedPatch = (patch * multiplier) + (stageBuckets[stage] ?? stageBuckets.alpha) + sequence
  const encoded = `${major}.${minor}.${encodedPatch}`

  return {
    version: encoded,
    preview: stage !== 'stable',
  }
}

function parsePrerelease(prerelease?: string) {
  if (!prerelease) {
    return { stage: 'stable' as const, sequence: 0 }
  }

  const [stageRaw, sequenceRaw] = prerelease.split('.')
  const stage = stageRaw === 'beta' || stageRaw === 'rc' || stageRaw === 'alpha' ? stageRaw : 'alpha'

View on GitHub (pinned to 27111382b4)

Solutions

  1. Sanitize the prerelease identifier before encoding: strip non-numeric suffixes and clamp negatives to 0.
  2. Fix parsePrerelease so it cannot emit a negative sequence (default to 0 when absent).
  3. Validate the prerelease tag shape upstream in the release script.

Example fix

// before
const { stage, sequence } = parsePrerelease(prerelease) // may yield -1

// after
const { stage, sequence } = parsePrerelease(prerelease)
const safeSequence = Math.max(0, sequence)
// then use safeSequence in the encodedPatch computation and the overflow check
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeSequence(seq: number): number {
  if (!Number.isFinite(seq) || seq < 0) return 0
  return Math.floor(seq)
}

Type guard

function isNonNegativeInt(n: unknown): n is number {
  return typeof n === 'number' && Number.isInteger(n) && n >= 0
}

Try / catch

try {
  return encodeNumericVersion(version)
}
catch (err) {
  if (/non-negative/i.test((err as Error).message)) {
    // strip malformed prerelease and retry without it
    return encodeNumericVersion(version.replace(/-.*$/, ''))
  }
  throw err
}

Prevention

When it happens

Trigger: A prerelease identifier like 'alpha.-1' or a parser branch that assigns -1 when no numeric segment is present; manual construction of a prerelease object with a negative sequence.

Common situations: parsePrerelease returns sequence 0 for missing identifiers, but a malformed tag (e.g. 'alpha.-5') or an upstream bug yields a negative number; unit tests feed adversarial prerelease strings.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/fc6f0060c41899b0. Report an issue: GitHub.