paperclipai/paperclip · error

projectId is required for project workspace diffs

Error message

projectId is required for project workspace diffs

What it means

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).

Source

Thrown at packages/plugins/plugin-workspace-diff/src/worker.ts:60

    : null;
}

const plugin = definePlugin({
  async setup(ctx) {
    ctx.logger.info(`${PLUGIN_NAME} plugin setup`);
    const workspaceDiff = workspaceDiffService();

    ctx.data.register("workspace-diff", async (params: Record<string, unknown>) => {
      const workspaceId = readString(params.workspaceId);
      const companyId = readString(params.companyId);
      if (!workspaceId || !companyId) {
        throw new Error("workspaceId and companyId are required");
      }

      if (params.entityType === "project_workspace") {
        const projectId = readString(params.projectId);
        if (!projectId) {
          throw new Error("projectId is required for project workspace diffs");
        }
        const workspaces = await ctx.projects.listWorkspaces(projectId, companyId);
        const workspace = workspaces.find((candidate) => candidate.id === workspaceId);
        if (!workspace) {
          throw new Error("Workspace not found");
        }
        return workspaceDiff.getDiff({
          id: workspace.id,
          companyId,
          cwd: workspace.path,
          baseRef: resolveDefaultBaseRef({
            projectWorkspaceDefaultRef: workspace.defaultRef,
            projectWorkspaceRepoRef: workspace.repoRef,
          }),
        }, workspaceDiffQuerySchema.parse(params));
      }

      const workspace = await ctx.executionWorkspaces.get(workspaceId, companyId);

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. When `entityType` is `"project_workspace"`, always include a non-empty `projectId`.
  2. 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.
  3. Validate the (entityType, projectId) pairing at the call site before dispatch.

Example fix

// before
ctx.data.fetch("workspace-diff", { workspaceId, companyId, entityType: "project_workspace" });
// after
ctx.data.fetch("workspace-diff", { workspaceId, companyId, entityType: "project_workspace", projectId });
Defensive patterns

Strategy: validation

Validate before calling

function assertProjectWorkspaceParams(input) {
  if (input.entityType !== "project_workspace") return input;
  const projectId = typeof input.projectId === "string" ? input.projectId.trim() : "";
  if (!projectId) {
    throw new TypeError("entityType 'project_workspace' requires a non-empty projectId");
  }
  return { ...input, projectId };
}

Type guard

function isCompleteProjectWorkspaceDiff(p): boolean {
  return p?.entityType !== "project_workspace" || (typeof p?.projectId === "string" && p.projectId.trim().length > 0);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/7a00e2b75c576c61. Report an issue: GitHub.