{"record":{"id":"ded0e89560b37746","repo":"paperclipai/paperclip","slug":"missing-project-id","errorCode":"missing_project_id","errorMessage":"Issue ${issue.identifier ?? issue.id} is linked to a project workspace but has no project id; refusing to launch ${input.adapterType} from fallback cwd.","messagePattern":"Issue (.+?) is linked to a project workspace but has no project id; refusing to launch (.+?) from fallback cwd\\.","errorType":"validation","errorClass":"WorkspaceValidationFailure","httpStatus":null,"severity":"error","filePath":"server/src/services/heartbeat.ts","lineNumber":2549,"sourceCode":"    // Preserve the closed failure code without copying subprocess output or\n    // credentials into the durable run. Setup recovery needs the actual cause.\n    if (isWorkspaceGitScanError(error)) throw new WorkspaceGitScanError(error.code, message);\n    throw new Error(message);\n  }\n\n  try {\n    await fs.rename(cloneTmpDir, cwd);\n  } catch (renameError) {\n    await fs\n      .rm(cloneTmpDir, { recursive: true, force: true })\n      .catch(() => undefined);\n    // The target appearing between the emptiness check and the rename means another\n    // materialization won the race; adopt its checkout instead of failing the run.\n    if (await hasAdoptableGitDir()) {\n      return { cwd, warning: null };\n    }\n    const reason =\n      renameError instanceof Error ? renameError.message : String(renameError);\n    throw new Error(\n      `Failed to move managed checkout into place at \"${cwd}\": ${reason}`,\n    );\n  }\n  return { cwd, warning: null };\n}\n\n/** Keep every distinct project repository inside the task's writable/synced root. */\nexport async function prepareProjectRepositoryWorkspaces(input: {\n  cwd: string;\n  anchorRepoUrl: string | null;\n  workspaces: Array<Pick<typeof projectWorkspaces.$inferSelect, \"id\" | \"repoUrl\" | \"repoRef\"> & { cwd?: string | null }>;\n  resolveGitAuth?: GitRemoteAuthProvider | null;\n}): Promise<Array<{ workspaceId: string; cwd: string; repoUrl: string; repoRef: string | null }>> {\n  const identity = (url: string) => url.trim().replace(/\\.git\\/?$/, \"\").replace(/\\/$/, \"\");\n  const seen = new Set(input.anchorRepoUrl ? [identity(input.anchorRepoUrl)] : []);\n  const selected = input.workspaces.filter((workspace) => {\n    if (!workspace.repoUrl || seen.has(identity(workspace.repoUrl))) return false;","sourceCodeStart":2531,"sourceCodeEnd":2567,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/heartbeat.ts#L2531-L2567","documentation":"Launch-safety guard in the heartbeat pre-launch workspace checks: the issue is linked to a project workspace but carries no projectId on the input, and the adapter is about to launch from the fallback cwd. Launching from the agent fallback directory would silently bypass the project workspace, so the run is refused instead.","triggerScenarios":"Thrown at server/src/services/heartbeat.ts:2134 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Link the issue to a project with a valid project id, or unset the project workspace link, then retry."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}