paperclipai/paperclip · info

Git update cancelled. Re-run with --yes to confirm executing

Error message

Git update cancelled. Re-run with --yes to confirm executing build scripts from the updated commit.

What it means

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.

Source

Thrown at cli/src/commands/update.ts:192

    if (mode !== "managed") throw new Error("--rollback is only available for managed installs.");
    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; }
    const next = await withInstallStoreLock(async () => rollbackManagedInstall(paths), paths);
    const restarted = await (overrides.restartActiveService ?? restartActiveManagedService)(next.version);
    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.`));
    return;
  }
  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; }
  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; }
  const request = resolveUpdateRequest(mode === "managed" ? manifest : null, options);
  if (mode === "managed" && manifest?.source === "git") {
    if (!manifest.repo || !manifest.ref || !manifest.sha) throw new Error("Managed git install metadata is incomplete.");
    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; }
    const targetSha = await resolveGitHubRef(manifest.repo, manifest.ref, runCommand);
    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; }
    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; }
    if (options.yes !== true) {
      const confirmed = await (overrides.confirm ?? defaultConfirm)(`Update from ${manifest.repo}@${manifest.ref} and execute build scripts from commit ${targetSha.slice(0, 12)}?`);
      if (!confirmed) throw new Error("Git update cancelled. Re-run with --yes to confirm executing build scripts from the updated commit.");
    }
    if (options.backup !== false) await runPreUpdateBackup(options, overrides.backup ?? (() => dbBackupCommand({})), overrides.hasInstanceData);
    const installed = await withInstallStoreLock(async () => {
      const payload = await installGitPayload(manifest.repo!, targetSha, runCommand, paths);
      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() };
      const next = buildNextManifest(record, manifest); const oldTarget = fs.readlinkSync(paths.currentPath); flipCurrentAtomic(payload.payloadPath, paths);
      try { writeInstallManifestAtomic(next, paths); } catch (error) { flipCurrentAtomic(path.resolve(paths.cliRoot, oldTarget), paths); throw error; }
      pruneInstallPayloads(next, paths); return payload;
    }, paths);
    let restarted: boolean;
    try {
      restarted = await (overrides.restartActiveService ?? restartActiveManagedService)(installed.version);
    } catch (error) {
      return rollbackAfterServiceValidationFailure(
        paths,
        overrides.restartActiveService ?? restartActiveManagedService,
        error,
        "Updated git payload",

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Re-run with explicit consent: `paperclipai update --yes`.
  2. Respond affirmatively at the interactive prompt (y/Enter) when run in a TTY.
  3. In CI/scripts always pass --yes (and ensure the repo/ref is trusted since build scripts execute).

Example fix

# before
paperclipai update
# after (non-interactive)
paperclipai update --yes
Defensive patterns

Strategy: validation

Validate before calling

// Ensure non-interactive contexts pass --yes for git updates
function resolveYesFlag(options: UpdateOptions, isTTY: boolean): boolean {
  return Boolean(options.yes) || isTTY ? Boolean(options.yes) : true; // auto-confirm in CI when trusted
}
// only auto-confirm when the repo/ref is trusted

Try / catch

try {
  await updateCommand(options);
} catch (error) {
  if (error instanceof Error && error.message === 'Git update cancelled. Re-run with --yes to confirm executing build scripts from the updated commit.') {
    // user-driven cancellation — only retry with --yes after explicit trust decision
    throw error;
  } else throw error;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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`.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/1475624b89ae46e6. Report an issue: GitHub.