paperclipai/paperclip · error · WorkspaceValidationFailure

persisted_workspace_missing_project_workspace_id

persisted_workspace_missing_project_workspace_id

Error message

Issue ${issue.identifier ?? issue.id} expected project workspace "${expectedProjectWorkspaceId}" but persisted execution workspace has no project workspace id.

What it means

Error "Issue ${issue.identifier ?? issue.id} expected project workspace "${expectedProjectWorkspaceId}" but persisted execution workspace has no project workspace id." thrown in paperclipai/paperclip.

Solutions

  1. Repair the persisted execution workspace so it records the expected project workspace id, or recreate it.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/services/heartbeat.ts:2134 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-08-18). Data as JSON: /api/errors/6ad91fc7788a7c68. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/heartbeat.ts:2549

    // Preserve the closed failure code without copying subprocess output or
    // credentials into the durable run. Setup recovery needs the actual cause.
    if (isWorkspaceGitScanError(error)) throw new WorkspaceGitScanError(error.code, message);
    throw new Error(message);
  }

  try {
    await fs.rename(cloneTmpDir, cwd);
  } catch (renameError) {
    await fs
      .rm(cloneTmpDir, { recursive: true, force: true })
      .catch(() => undefined);
    // The target appearing between the emptiness check and the rename means another
    // materialization won the race; adopt its checkout instead of failing the run.
    if (await hasAdoptableGitDir()) {
      return { cwd, warning: null };
    }
    const reason =
      renameError instanceof Error ? renameError.message : String(renameError);
    throw new Error(
      `Failed to move managed checkout into place at "${cwd}": ${reason}`,
    );
  }
  return { cwd, warning: null };
}

/** Keep every distinct project repository inside the task's writable/synced root. */
export async function prepareProjectRepositoryWorkspaces(input: {
  cwd: string;
  anchorRepoUrl: string | null;
  workspaces: Array<Pick<typeof projectWorkspaces.$inferSelect, "id" | "repoUrl" | "repoRef"> & { cwd?: string | null }>;
  resolveGitAuth?: GitRemoteAuthProvider | null;
}): Promise<Array<{ workspaceId: string; cwd: string; repoUrl: string; repoRef: string | null }>> {
  const identity = (url: string) => url.trim().replace(/\.git\/?$/, "").replace(/\/$/, "");
  const seen = new Set(input.anchorRepoUrl ? [identity(input.anchorRepoUrl)] : []);
  const selected = input.workspaces.filter((workspace) => {
    if (!workspace.repoUrl || seen.has(identity(workspace.repoUrl))) return false;

View on GitHub (pinned to 3f1d897a7c)