{"record":{"id":"1475624b89ae46e6","repo":"paperclipai/paperclip","slug":"git-update-cancelled-re-run-with-yes-to-confirm","errorCode":null,"errorMessage":"Git update cancelled. Re-run with --yes to confirm executing build scripts from the updated commit.","messagePattern":"Git update cancelled\\. Re-run with --yes to confirm executing build scripts from the updated commit\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"cli/src/commands/update.ts","lineNumber":192,"sourceCode":"    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.\");\n    }\n    if (options.backup !== false) await runPreUpdateBackup(options, overrides.backup ?? (() => dbBackupCommand({})), overrides.hasInstanceData);\n    const installed = await withInstallStoreLock(async () => {\n      const payload = await installGitPayload(manifest.repo!, targetSha, runCommand, paths);\n      const record: InstallRecord = { source: \"git\", version: payload.version, channel: \"pinned\", repo: manifest.repo, ref: manifest.ref, sha: targetSha, payloadPath: payload.payloadPath, installedAt: (overrides.now?.() ?? new Date()).toISOString() };\n      const next = buildNextManifest(record, manifest); const oldTarget = fs.readlinkSync(paths.currentPath); flipCurrentAtomic(payload.payloadPath, paths);\n      try { writeInstallManifestAtomic(next, paths); } catch (error) { flipCurrentAtomic(path.resolve(paths.cliRoot, oldTarget), paths); throw error; }\n      pruneInstallPayloads(next, paths); return payload;\n    }, paths);\n    let restarted: boolean;\n    try {\n      restarted = await (overrides.restartActiveService ?? restartActiveManagedService)(installed.version);\n    } catch (error) {\n      return rollbackAfterServiceValidationFailure(\n        paths,\n        overrides.restartActiveService ?? restartActiveManagedService,\n        error,\n        \"Updated git payload\",","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/commands/update.ts#L174-L210","documentation":"Thrown by updateCommand on the git-payload path when the interactive confirmation prompt (defaultConfirm or an injected confirm override) returns false, i.e. the user declined to approve fetching and executing build scripts from the new commit. Because git payloads run arbitrary build scripts, explicit consent is required; --yes bypasses the prompt.","triggerScenarios":"A managed git install with a movable ref (not a hex sha) whose resolved targetSha differs from manifest.sha, options.yes is not true, and the confirm callback returns false. Also thrown in non-TTY environments where defaultConfirm always returns false unless --yes is set.","commonSituations":"Running `paperclipai update` interactively and pressing 'n' or Esc at the prompt. Piping into the command (no TTY) so defaultConfirm auto-returns false. CI/automation that forgot `--yes`.","solutions":["Re-run with explicit consent: `paperclipai update --yes`.","Respond affirmatively at the interactive prompt (y/Enter) when run in a TTY.","In CI/scripts always pass --yes (and ensure the repo/ref is trusted since build scripts execute)."],"exampleFix":"# before\npaperclipai update\n# after (non-interactive)\npaperclipai update --yes","handlingStrategy":"validation","validationCode":"// Ensure non-interactive contexts pass --yes for git updates\nfunction resolveYesFlag(options: UpdateOptions, isTTY: boolean): boolean {\n  return Boolean(options.yes) || isTTY ? Boolean(options.yes) : true; // auto-confirm in CI when trusted\n}\n// only auto-confirm when the repo/ref is trusted","typeGuard":null,"tryCatchPattern":"try {\n  await updateCommand(options);\n} catch (error) {\n  if (error instanceof Error && error.message === 'Git update cancelled. Re-run with --yes to confirm executing build scripts from the updated commit.') {\n    // user-driven cancellation — only retry with --yes after explicit trust decision\n    throw error;\n  } else throw error;\n}","preventionTips":["In CI/automation always pass --yes for trusted git refs; defaultConfirm returns false without a TTY.","Treat git payloads as code execution — only --yes repos/refs you control.","Pin a git install to a hex sha to avoid the confirmation prompt entirely (pinned installs short-circuit)."],"tags":["git","update","confirmation","interactive"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}