paperclipai/paperclip · info

[paperclip] Workspace restore preserved local working tree c

Error message

[paperclip] Workspace restore preserved local working tree changes after clean sandbox restore.

What it means

resetLocalGitIndexToHead runs `git reset --quiet HEAD -- .` to unstage everything after a sandbox restore, asserts no staged changes remain (throws if staged diff is non-empty), then — only when checkWorkingTreeClean is set — runs `git diff --name-status HEAD`. Non-empty output means the restore left unstaged working-tree modifications, which the helper intentionally does NOT discard; this console.warn says those local changes were preserved through the clean-restore flow.

Source

Thrown at packages/adapter-utils/src/git-workspace-sync.ts:638

  const stagedDiff = await runLocalGit(input.localDir, ["diff", "--cached", "--name-status", "HEAD", "--"], {
    timeout: 10_000,
    maxBuffer: 1024 * 1024,
  });
  if (stagedDiff.stdout.trim().length > 0) {
    throw new Error(
      `Workspace restore left staged git index changes after reset:\n${stagedDiff.stdout.trim()}`,
    );
  }

  if (!input.checkWorkingTreeClean) return;

  const workingTreeDiff = await runLocalGit(input.localDir, ["diff", "--name-status", "HEAD", "--"], {
    timeout: 10_000,
    maxBuffer: 1024 * 1024,
  });
  if (workingTreeDiff.stdout.trim().length > 0) {
    console.warn(
      "[paperclip] Workspace restore preserved local working tree changes after clean sandbox restore.",
    );
  }
}

View on GitHub (pinned to 120ae5428f)

Solutions

  1. Inspect what survived: run `git status --porcelain` and `git diff` in the workspace directory.
  2. If you need a pristine tree, commit or stash the local changes, or explicitly clean them (git checkout -- . / git clean).
  3. Treat the message as expected behavior when local edits existed before the restore — data was deliberately preserved.
Defensive patterns

Strategy: validation

Validate before calling

import { execSync } from "node:child_process";
const dirty = execSync("git status --porcelain", { cwd: workspaceDir, encoding: "utf8" });
if (dirty.trim().length > 0) {
  // decide before restore: commit, stash, or accept that restore preserves these edits
}

Type guard

const isCleanStatus = (porcelain: string): boolean => porcelain.trim().length === 0;

Prevention

When it happens

Trigger: A workspace that had local uncommitted edits (hotfixes, generated files) goes through a clean sandbox restore; the index resets to HEAD but the modified files remain on disk, so `git diff HEAD` still reports them.

Common situations: Agent run restored from a sandbox while a developer had local edits in the same checkout; generated artifacts differing from HEAD; end-of-line or permission-only diffs.

Related errors


AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18). Data as JSON: /api/errors/584db90a459541f0. Report an issue: GitHub.