{"record":{"id":"e3ec86c82fa68a13","repo":"paperclipai/paperclip","slug":"choose-only-one-of-latest-canary-or-versio","errorCode":null,"errorMessage":"Choose only one of --latest, --canary, or --version.","messagePattern":"Choose only one of --latest, --canary, or --version\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/src/commands/update.ts","lineNumber":119,"sourceCode":"  const bParts = b.prerelease.split(\".\");\n  for (let index = 0; index < Math.max(aParts.length, bParts.length); index += 1) {\n    const leftPart = aParts[index];\n    const rightPart = bParts[index];\n    if (leftPart === undefined) return -1;\n    if (rightPart === undefined) return 1;\n    if (leftPart === rightPart) continue;\n    const leftNumeric = /^\\d+$/.test(leftPart);\n    const rightNumeric = /^\\d+$/.test(rightPart);\n    if (leftNumeric && rightNumeric) return Math.sign(Number(leftPart) - Number(rightPart));\n    if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1;\n    return leftPart < rightPart ? -1 : 1;\n  }\n  return 0;\n}\n\nexport function resolveUpdateRequest(manifest: InstallManifest | null, options: Pick<UpdateOptions, \"canary\" | \"latest\" | \"version\">): { spec: string; channel: InstallChannel; explicit: boolean } {\n  const selected = Number(Boolean(options.canary)) + Number(Boolean(options.latest)) + Number(Boolean(options.version));\n  if (selected > 1) throw new Error(\"Choose only one of --latest, --canary, or --version.\");\n  if (options.version) return { spec: options.version.trim(), channel: \"pinned\", explicit: true };\n  if (options.canary) return { spec: \"canary\", channel: \"canary\", explicit: true };\n  if (options.latest) return { spec: \"latest\", channel: \"latest\", explicit: true };\n  if (manifest?.channel === \"pinned\") return { spec: manifest.version, channel: \"pinned\", explicit: false };\n  const channel = manifest?.channel === \"canary\" ? \"canary\" : \"latest\";\n  return { spec: channel, channel, explicit: false };\n}\n\nexport function rollbackManagedInstall(paths = resolveInstallStorePaths()): InstallManifest {\n  const manifest = readInstallManifest(paths);\n  if (!manifest) throw new Error(\"No managed install was found to roll back.\");\n  const target = manifest.previous[0];\n  if (!target) throw new Error(\"No previous managed payload is available for rollback.\");\n  if (!fs.existsSync(target.payloadPath)) throw new Error(`Previous payload is missing: ${target.payloadPath}`);\n  const current: InstallRecord = { source: manifest.source, version: manifest.version, channel: manifest.channel, payloadPath: manifest.payloadPath, repo: manifest.repo, ref: manifest.ref, sha: manifest.sha, installedAt: manifest.installedAt };\n  const next: InstallManifest = { schemaVersion: manifest.schemaVersion, ...target, previous: [current, ...manifest.previous.slice(1)].slice(0, 2) };\n  const oldTarget = fs.readlinkSync(paths.currentPath);\n  flipCurrentAtomic(target.payloadPath, paths);","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/commands/update.ts#L101-L137","documentation":"Thrown by resolveUpdateRequest when more than one of the mutually-exclusive channel selectors --latest, --canary, and --version is supplied at once. The update command needs a single unambiguous target channel; passing two or more makes the intent indeterminate. The check sums the boolean count of the three flags and rejects when the total exceeds one.","triggerScenarios":"Invoking `paperclipai update --latest --canary`, `--latest --version 1.2.3`, `--canary --version 1.2.3`, or all three together. Also triggered by programmatic callers of updateCommand/resolveUpdateRequest passing an UpdateOptions object with two or more of those fields truthy.","commonSituations":"Copy-pasting flags from different docs/tutorials. Scripted invocations concatenating flags conditionally without mutual exclusion. Shell aliases that bake in --latest combined with a manual --version on the command line.","solutions":["Pass exactly one channel flag: `--latest`, `--canary`, or `--version <semver>`.","Drop all three to use the manifest's recorded channel (auto-detected from the prior install).","Audit your shell alias / wrapper script and remove the conflicting flag."],"exampleFix":"# before\npaperclipai update --latest --version 1.2.3\n# after\npaperclipai update --version 1.2.3","handlingStrategy":"validation","validationCode":"// Reject conflicting channel flags before calling resolveUpdateRequest\nfunction assertSingleUpdateChannel(options: { canary?: boolean; latest?: boolean; version?: string }): void {\n  const n = Number(Boolean(options.canary)) + Number(Boolean(options.latest)) + Number(Boolean(options.version));\n  if (n > 1) throw new Error('Pass only one of --latest, --canary, --version');\n}\n// assertSingleUpdateChannel(options); resolveUpdateRequest(manifest, options);","typeGuard":null,"tryCatchPattern":"try {\n  resolveUpdateRequest(manifest, options);\n} catch (error) {\n  if (error instanceof Error && error.message === 'Choose only one of --latest, --canary, or --version.') {\n    // pick one channel by priority and retry\n    const fixed = { version: options.version, canary: !options.version && options.canary, latest: !options.version && !options.canary && options.latest };\n    resolveUpdateRequest(manifest, fixed);\n  } else throw error;\n}","preventionTips":["In wrapper scripts, derive the channel from a single variable rather than concatenating flags.","Use a CLI arg parser that declares the three flags in a mutually-exclusive group.","Document the precedence so users do not stack --latest with --version."],"tags":["update","cli-args","validation"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}