{"record":{"id":"fc6f0060c41899b0","repo":"moeru-ai/airi","slug":"prerelease-sequence-must-be-non-negative-sequen","errorCode":null,"errorMessage":"Prerelease sequence must be non-negative: ${sequence}","messagePattern":"Prerelease sequence must be non-negative: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integrations/vscode/vscode-airi/scripts/shared.ts","lineNumber":71,"sourceCode":"  const minor = Number.parseInt(match.groups.minor, 10)\n  const patch = Number.parseInt(match.groups.patch, 10)\n  const prerelease = match.groups.pre\n\n  const multiplier = 10_000\n  const stageBuckets = {\n    alpha: 1_000,\n    beta: 2_000,\n    rc: 3_000,\n    stable: 9_000,\n  } as const\n\n  const { stage, sequence } = parsePrerelease(prerelease)\n  const maxSequence = (multiplier - 1) - stageBuckets[stage]\n  if (sequence > maxSequence) {\n    throw new Error(`Prerelease sequence overflow for ${stage}: ${sequence} exceeds limit ${maxSequence}`)\n  }\n  if (sequence < 0) {\n    throw new Error(`Prerelease sequence must be non-negative: ${sequence}`)\n  }\n\n  const encodedPatch = (patch * multiplier) + (stageBuckets[stage] ?? stageBuckets.alpha) + sequence\n  const encoded = `${major}.${minor}.${encodedPatch}`\n\n  return {\n    version: encoded,\n    preview: stage !== 'stable',\n  }\n}\n\nfunction parsePrerelease(prerelease?: string) {\n  if (!prerelease) {\n    return { stage: 'stable' as const, sequence: 0 }\n  }\n\n  const [stageRaw, sequenceRaw] = prerelease.split('.')\n  const stage = stageRaw === 'beta' || stageRaw === 'rc' || stageRaw === 'alpha' ? stageRaw : 'alpha'","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/integrations/vscode/vscode-airi/scripts/shared.ts#L53-L89","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Sanitize the prerelease identifier before encoding: strip non-numeric suffixes and clamp negatives to 0.","Fix parsePrerelease so it cannot emit a negative sequence (default to 0 when absent).","Validate the prerelease tag shape upstream in the release script."],"exampleFix":"// before\nconst { stage, sequence } = parsePrerelease(prerelease) // may yield -1\n\n// after\nconst { stage, sequence } = parsePrerelease(prerelease)\nconst safeSequence = Math.max(0, sequence)\n// then use safeSequence in the encodedPatch computation and the overflow check","handlingStrategy":"validation","validationCode":"function sanitizeSequence(seq: number): number {\n  if (!Number.isFinite(seq) || seq < 0) return 0\n  return Math.floor(seq)\n}","typeGuard":"function isNonNegativeInt(n: unknown): n is number {\n  return typeof n === 'number' && Number.isInteger(n) && n >= 0\n}","tryCatchPattern":"try {\n  return encodeNumericVersion(version)\n}\ncatch (err) {\n  if (/non-negative/i.test((err as Error).message)) {\n    // strip malformed prerelease and retry without it\n    return encodeNumericVersion(version.replace(/-.*$/, ''))\n  }\n  throw err\n}","preventionTips":["Make parsePrerelease default to 0 instead of emitting a negative sequence.","Reject prerelease identifiers like 'alpha.-1' at the source.","Unit-test parsePrerelease against adversarial inputs."],"tags":["semver","validation","parser","build"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}