{"record":{"id":"3ce20eeda535e337","repo":"paperclipai/paperclip","slug":"rollback-is-only-available-for-managed-installs","errorCode":null,"errorMessage":"--rollback is only available for managed installs.","messagePattern":"--rollback is only available for managed installs\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/src/commands/update.ts","lineNumber":174,"sourceCode":"  try {\n    await restartActiveService(rolledBack.version);\n  } catch (restartError) {\n    throw new Error(\n      `${payloadLabel} failed service validation and was rolled back to ${rolledBack.version}, but the rolled-back service also failed to restart.`,\n      { cause: new AggregateError([validationError, restartError]) },\n    );\n  }\n  throw new Error(`${payloadLabel} failed service validation and was rolled back to ${rolledBack.version}.`, { cause: validationError });\n}\n\nexport async function updateCommand(options: UpdateOptions, overrides: Partial<Dependencies> = {}): Promise<void> {\n  const paths = overrides.paths ?? resolveInstallStorePaths();\n  const executablePath = overrides.executablePath ?? process.argv[1] ?? \"\";\n  const runCommand = overrides.runCommand ?? execFileAsync;\n  const mode = detectInstallMode(executablePath, paths);\n  const manifest = readInstallManifest(paths);\n  if (options.rollback) {\n    if (mode !== \"managed\") throw new Error(\"--rollback is only available for managed installs.\");\n    if (options.dryRun) { emit(options, { mode, action: \"rollback\", dryRun: true, target: manifest?.previous[0]?.version ?? null }, `Would roll back to ${manifest?.previous[0]?.version ?? \"the previous payload\"}.`); return; }\n    const next = await withInstallStoreLock(async () => rollbackManagedInstall(paths), paths);\n    const restarted = await (overrides.restartActiveService ?? restartActiveManagedService)(next.version);\n    emit(options, { mode, action: \"rollback\", version: next.version, restarted }, pc.green(`Rolled back to paperclipai ${next.version}${restarted ? \" and restarted the active service\" : \"\"}. Database migrations are not reversed; restore the pre-update backup if needed.`));\n    return;\n  }\n  if (mode === \"npx\") { emit(options, { mode, action: \"install\" }, \"This is an ephemeral npx install. Run `paperclipai install`, then use `paperclipai update` from the managed shim.\"); return; }\n  if (mode === \"source\" || mode === \"unknown\") { emit(options, { mode, action: \"manual\" }, \"This appears to be a source checkout. Update it with `git pull` followed by `pnpm install`; Paperclip will not mutate the repository.\"); return; }\n  const request = resolveUpdateRequest(mode === \"managed\" ? manifest : null, options);\n  if (mode === \"managed\" && manifest?.source === \"git\") {\n    if (!manifest.repo || !manifest.ref || !manifest.sha) throw new Error(\"Managed git install metadata is incomplete.\");\n    if (/^[0-9a-f]{7,40}$/i.test(manifest.ref)) { emit(options, { mode, source: \"git\", pinned: true, sha: manifest.sha }, `Git install is pinned at ${manifest.sha.slice(0, 12)}.`); return; }\n    const targetSha = await resolveGitHubRef(manifest.repo, manifest.ref, runCommand);\n    if (targetSha === manifest.sha) { emit(options, { mode, source: \"git\", changed: false, sha: targetSha, ref: manifest.ref }, `${manifest.repo}@${manifest.ref} is already at ${targetSha.slice(0, 12)}.`); return; }\n    if (options.check || options.dryRun) { emit(options, { mode, source: \"git\", changed: true, currentSha: manifest.sha, targetSha, ref: manifest.ref, dryRun: Boolean(options.dryRun) }, `Git update available: ${manifest.sha.slice(0, 12)} → ${targetSha.slice(0, 12)}.`); if (options.check) process.exitCode = 10; return; }\n    if (options.yes !== true) {\n      const confirmed = await (overrides.confirm ?? defaultConfirm)(`Update from ${manifest.repo}@${manifest.ref} and execute build scripts from commit ${targetSha.slice(0, 12)}?`);\n      if (!confirmed) throw new Error(\"Git update cancelled. Re-run with --yes to confirm executing build scripts from the updated commit.\");","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/commands/update.ts#L156-L192","documentation":"Thrown by updateCommand when the user passes --rollback but detectInstallMode does not report 'managed'. Rollback only manipulates the managed install store (current symlink + manifest previous[]), so it is meaningless for global npm installs, ephemeral npx runs, source checkouts, or unrecognized modes. The error fires before any filesystem mutation.","triggerScenarios":"Running `paperclipai update --rollback` from an npx invocation, a `npm install -g paperclipai` global install, a git source checkout, or any path detectInstallMode classifies as 'npx', 'global-npm', 'source', or 'unknown'.","commonSituations":"User installed via npm globally and expects rollback to work. Running the CLI via `npx paperclipai update --rollback`. Running from a cloned repo. PATH pointing at a non-managed binary.","solutions":["Switch to a managed install first: `paperclipai install`, then `paperclipai update --rollback`.","For global-npm, downgrade explicitly with `paperclipai update --version <older>` instead of --rollback.","For npx, the install is ephemeral — reinstall the desired version directly.","For source checkouts, use `git checkout <tag>` to change versions."],"exampleFix":"# before (globally installed)\npaperclipai update --rollback\n# after\npaperclipai update --version 1.2.3","handlingStrategy":"validation","validationCode":"import { detectInstallMode, resolveInstallStorePaths } from '../commands/update.js';\nfunction canRollback(): boolean {\n  return detectInstallMode(process.argv[1] ?? '', resolveInstallStorePaths()) === 'managed';\n}\n// if (!canRollback()) { /* use update --version or install managed first */ }","typeGuard":null,"tryCatchPattern":"try {\n  await updateCommand({ rollback: true });\n} catch (error) {\n  if (error instanceof Error && error.message === '--rollback is only available for managed installs.') {\n    // fall back to a direct version install for non-managed modes\n    await updateCommand({ version: desiredVersion, yes: true });\n  } else throw error;\n}","preventionTips":["Use `paperclipai install` to get a managed install if you want rollback support.","Remember rollback is a managed-store feature only — global npm and npx have no payload history.","Check the install mode with `paperclipai update --check` or detectInstallMode before scripting rollback."],"tags":["rollback","install-mode","update"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}