nexu-io/open-design · error · Error

design system backing project belongs to another workspace

Error message

design system backing project belongs to another workspace

What it means

Thrown by the design-system backing project preparer when the resolved project's binding carries a `workspaceId` that is set and differs from the requesting principal's `teamId`. This is the cross-workspace contamination guard: a backing project bound under one workspace cannot be moved by a share request originating from another workspace.

Source

Thrown at apps/daemon/src/design-systems/team-project-share.ts:102

 * and each remote-project/local-projection pair compensates itself before
 * rejecting.
 */
export function createDesignSystemBackingProjectPreparer(
  options: CreateDesignSystemBackingProjectPreparerOptions,
): CreateLinkedProjectTeamResourceShareServiceOptions['prepare'] {
  return async (resourceId, scope) => {
    let projectId = (await options.resolveProjectId(resourceId, scope))?.trim() ?? '';
    if ((!projectId || !options.projectExists(projectId)) && options.ensureProjectId) {
      projectId = (await options.ensureProjectId(resourceId, scope))?.trim() ?? '';
    }
    if (!projectId || !options.projectExists(projectId)) {
      throw new Error('design system backing project is unavailable');
    }
    const workspaceId = scope.principal.teamId;
    const memberId = scope.principal.memberId;
    const binding = options.getProjectBinding(projectId);
    if (binding?.workspaceId && binding.workspaceId !== workspaceId) {
      throw new Error('design system backing project belongs to another workspace');
    }
    if (binding?.createdByWorkspaceMemberId !== memberId) {
      throw new TeamResourceShareForbiddenError();
    }
    options.onPrepared?.({ resourceId, projectId, scope });
    return {
      projectId,
      transition: async (visibility) => {
        if (visibility === 'team') {
          const published = await options.publishProject(projectId, scope);
          if (published.version == null) {
            throw new Error('design system backing project publish failed');
          }
          try {
            await options.persistVisibility({ projectId, scope, visibility });
          } catch (error) {
            try {
              await options.unpublishProject(projectId, scope);

View on GitHub (pinned to 5be4028344)

Solutions

  1. Confirm the requesting user is acting in the same workspace that owns the backing project binding.
  2. If the binding workspace is stale due to a migration, correct the binding row so `workspaceId` matches the owning workspace.
  3. Do not attempt to cross-share a project from another workspace; re-create the design system resource under the correct workspace instead.
Defensive patterns

Strategy: validation

Validate before calling

// Before sharing, confirm workspace ownership:
const binding = getProjectBinding(projectId);
if (binding?.workspaceId && binding.workspaceId !== scope.principal.teamId) {
  throw new Error('refusing cross-workspace share');
}
await share(resourceId, scope);

Try / catch

try {
  await linkedShare.share(resourceId, scope);
} catch (err) {
  if (err instanceof Error && /belongs to another workspace/.test(err.message)) {
    // re-create the design system under the correct workspace
  }
  throw err;
}

Prevention

When it happens

Trigger: A share/unshare request whose `scope.principal.teamId` is workspace B, but the backing project binding's `workspaceId` is workspace A (a different, non-empty value).

Common situations: A design system was originally shared under workspace A and is now being operated on from workspace B due to a stale client, a workspace switch, or a binding row that was not migrated. Manual database edits that changed a binding's workspace. A resource id collision resolving to the wrong project.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/86b51ed6a046e3be. Report an issue: GitHub.