paperclipai/paperclip · error · Error

Workspace database repair command timed out during ${repairP

Error message

Workspace database repair command timed out during ${repairPhase}.

What it means

Thrown when the repair reseed child process exceeds the hard 20-minute budget. The route spawns node <cli> worktree reseed ... and arms a timer (server/src/routes/execution-workspaces.ts:715-720) that SIGTERMs the child at 20 minutes and SIGKILLs 5 seconds later; a non-zero exit with repairCommandTimedOut=true produces this message, naming the outer repair phase (target_backup or full_reseed) the flow had reached. The worktree itself is preserved (worktreePreserved, databaseOnly) and the failed phase is reported on the workspace operation.

Source

Thrown at server/src/routes/execution-workspaces.ts:775

              let seedFailurePhase: string | null = null;
              try {
                const failedManifest = JSON.parse(readFileSync(manifestPath, "utf8")) as {
                  phase?: unknown;
                  state?: unknown;
                };
                seedFailurePhase = failedManifest.state === "failed" && typeof failedManifest.phase === "string"
                  ? failedManifest.phase
                  : null;
              } catch {
                // The outer repair phase remains exact when no seed phase was persisted.
              }
              await reportProgress({
                metadata: { seedFailurePhase },
                system: seedFailurePhase ? `Workspace seed failed during ${seedFailurePhase}.\n` : null,
              });
              throw new Error(
                repairCommandTimedOut
                  ? `Workspace database repair command timed out during ${repairPhase}.`
                  : `Workspace database repair command failed during ${repairPhase}.`,
              );
            }
            if (!reseedObserved) {
              await reportRepairPhase("target_backup", "succeeded");
              await reportRepairPhase("full_reseed", "started");
            }

            const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as Record<string, unknown>;
            if (!isVerifiedWorktreeSeedManifest(manifest)) {
              throw new Error("Workspace reseed returned without a verified terminal manifest.");
            }
            const expectedInstanceId = resolveManagedWorkspaceInstanceId(workspaceCwd);
            if (manifest.targetInstanceId !== expectedInstanceId) {
              throw repairPreconditionError(
                422,
                "seed_manifest_instance_mismatch",
                "Verified seed manifest belongs to a different workspace instance.",

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Check the workspace operation record and captured child stdout/stderr to see how far the reseed got before the kill.
  2. Eliminate I/O contention: stop concurrent repairs/seeds against the same base, repair from faster local disk.
  3. Shrink the seed scope if the CLI supports it (seed mode filters), or prune/archieve old data in the base workspace database.
  4. Retry the repair — the target is backed up first (--backup-target) so a timed-out attempt is safe to re-run.
  5. If large databases are the norm, raise the fixed 20-minute budget in server/src/routes/execution-workspaces.ts:715 as a code change.
Defensive patterns

Strategy: retry

Validate before calling

// Estimate before repair: check base .paperclip database size on disk
const sizeBytes = await du(path.join(baseCwd, ".paperclip"));
if (sizeBytes > LARGE_SEED_THRESHOLD) { /* schedule during low load, or prune base data first */ }

Try / catch

try {
  await repair(id);
} catch (err) {
  if (/timed out during/.test(String(err?.body?.error ?? err?.message ?? ""))) {
    // reseed target is backed up and worktree preserved — safe to retry after reducing I/O load
    await waitForIdleIo(); await repair(id);
  } else throw err;
}

Prevention

When it happens

Trigger: POST repair where the full reseed of the worktree database takes longer than 20 minutes: very large base database, slow disk/network-attached storage, lock contention on the base .paperclip database, or the child hanging on a prompt because --yes did not cover an interactive path. The interval poll (manifestPoll) determines whether the phase is reported as full_reseed vs target_backup.

Common situations: Base workspace with a multi-GB PGlite/pg data directory copied over NFS; a seeded company with huge issue/comment history; concurrent repairs or an active seed against the same base starving I/O; a CLI version that stalls waiting for a lock on the source database.

Understand the failure class

Related errors


AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21). Data as JSON: /api/errors/3a3f2eb8383a49ca. Report an issue: GitHub.