{"record":{"id":"7a00e2b75c576c61","repo":"paperclipai/paperclip","slug":"projectid-is-required-for-project-workspace-diffs","errorCode":null,"errorMessage":"projectId is required for project workspace diffs","messagePattern":"projectId is required for project workspace diffs","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/plugins/plugin-workspace-diff/src/worker.ts","lineNumber":60,"sourceCode":"    : null;\n}\n\nconst plugin = definePlugin({\n  async setup(ctx) {\n    ctx.logger.info(`${PLUGIN_NAME} plugin setup`);\n    const workspaceDiff = workspaceDiffService();\n\n    ctx.data.register(\"workspace-diff\", async (params: Record<string, unknown>) => {\n      const workspaceId = readString(params.workspaceId);\n      const companyId = readString(params.companyId);\n      if (!workspaceId || !companyId) {\n        throw new Error(\"workspaceId and companyId are required\");\n      }\n\n      if (params.entityType === \"project_workspace\") {\n        const projectId = readString(params.projectId);\n        if (!projectId) {\n          throw new Error(\"projectId is required for project workspace diffs\");\n        }\n        const workspaces = await ctx.projects.listWorkspaces(projectId, companyId);\n        const workspace = workspaces.find((candidate) => candidate.id === workspaceId);\n        if (!workspace) {\n          throw new Error(\"Workspace not found\");\n        }\n        return workspaceDiff.getDiff({\n          id: workspace.id,\n          companyId,\n          cwd: workspace.path,\n          baseRef: resolveDefaultBaseRef({\n            projectWorkspaceDefaultRef: workspace.defaultRef,\n            projectWorkspaceRepoRef: workspace.repoRef,\n          }),\n        }, workspaceDiffQuerySchema.parse(params));\n      }\n\n      const workspace = await ctx.executionWorkspaces.get(workspaceId, companyId);","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/plugins/plugin-workspace-diff/src/worker.ts#L42-L78","documentation":"Thrown by the workspace-diff plugin (worker.ts:60) only when `params.entityType === \"project_workspace\"` and `projectId` is missing. Project-workspace diffs must enumerate a project's workspaces via `ctx.projects.listWorkspaces(projectId, companyId)`, so a projectId is mandatory in that branch (it is optional for plain execution workspaces).","triggerScenarios":"Requesting a diff with `entityType: \"project_workspace\"` but omitting `projectId`, or passing an empty/non-string projectId. The check fires after the workspaceId/companyId guard, so those must already be present.","commonSituations":"A caller sets entityType to project_workspace to get a primary-repo diff but forgets projectId, or reuses an execution-workspace params object (which lacks projectId) for a project-workspace diff.","solutions":["When `entityType` is `\"project_workspace\"`, always include a non-empty `projectId`.","If you actually want an execution-workspace diff, omit `entityType` (or set it to anything other than `\"project_workspace\"`) and the projectId requirement does not apply.","Validate the (entityType, projectId) pairing at the call site before dispatch."],"exampleFix":"// before\nctx.data.fetch(\"workspace-diff\", { workspaceId, companyId, entityType: \"project_workspace\" });\n// after\nctx.data.fetch(\"workspace-diff\", { workspaceId, companyId, entityType: \"project_workspace\", projectId });","handlingStrategy":"validation","validationCode":"function assertProjectWorkspaceParams(input) {\n  if (input.entityType !== \"project_workspace\") return input;\n  const projectId = typeof input.projectId === \"string\" ? input.projectId.trim() : \"\";\n  if (!projectId) {\n    throw new TypeError(\"entityType 'project_workspace' requires a non-empty projectId\");\n  }\n  return { ...input, projectId };\n}","typeGuard":"function isCompleteProjectWorkspaceDiff(p): boolean {\n  return p?.entityType !== \"project_workspace\" || (typeof p?.projectId === \"string\" && p.projectId.trim().length > 0);\n}","tryCatchPattern":null,"preventionTips":["Treat (entityType === 'project_workspace') as requiring projectId by contract.","For execution-workspace diffs, omit entityType entirely.","Validate entityType/projectId pairing at the UI action dispatch boundary."],"tags":["plugin","workspace-diff","validation","param-validation","project-workspace"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}