paperclipai/paperclip · error · HttpError

cross_company_workspace

cross_company_workspace

Error message

Project workspace belongs to another company

What it means

Cross-tenant guard in targetProjectWorkspaceCandidate: the project/workspace ids supplied for an explicit workspace target resolve to rows owned by a different company than the issue's. The mismatch is rejected before any path resolution so one tenant can never address another tenant's workspace files.

Source

Thrown at server/src/services/workspace-file-resources.ts:1056

    return issue;
  }

  async function targetProjectWorkspaceCandidate(
    issue: IssueRow,
    target: WorkspaceTargetInput,
  ): Promise<WorkspaceCandidate | null> {
    const projectId = target.projectId ?? null;
    const workspaceId = target.workspaceId ?? null;
    if (!projectId && !workspaceId) return null;
    if (!projectId || !workspaceId) {
      throw unprocessable("Workspace file target requires both projectId and workspaceId", { code: "invalid_target" });
    }

    const [project] = await db.select().from(projects).where(eq(projects.id, projectId)).limit(1);
    const [workspace] = await db.select().from(projectWorkspaces).where(eq(projectWorkspaces.id, workspaceId)).limit(1);
    if (!project || !workspace) throw notFound("Project workspace not found");
    if (project.companyId !== issue.companyId || workspace.companyId !== issue.companyId) {
      throw new HttpError(403, "Project workspace belongs to another company", { code: "cross_company_workspace" });
    }
    if (workspace.projectId !== project.id) {
      throw unprocessable("Workspace does not belong to the selected project", { code: "workspace_project_mismatch" });
    }

    return candidateFromProjectWorkspace(workspace, { id: project.id, name: project.name });
  }

  async function listCandidates(
    issue: IssueRow,
    selector: WorkspaceFileSelector,
    target: WorkspaceTargetInput = {},
  ): Promise<WorkspaceCandidate[]> {
    const explicitTarget = await targetProjectWorkspaceCandidate(issue, target);
    if (explicitTarget) return [explicitTarget];

    const candidates: WorkspaceCandidate[] = [];
    if ((selector === "auto" || selector === "execution") && issue.projectId) {

View on GitHub (pinned to 120ae5428f)

Solutions

  1. Access the workspace through a project that belongs to your company; cross-company workspace access is forbidden.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/services/workspace-file-resources.ts:1056 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18). Data as JSON: /api/errors/44ce27d6d443d388. Report an issue: GitHub.