nexu-io/open-design · error · TeamResourceShareForbiddenError

workspace_resource_share_denied

Error message

workspace_resource_share_denied

What it means

Thrown as `TeamResourceShareForbiddenError` (message `workspace_resource_share_denied`) when the backing project binding's `createdByWorkspaceMemberId` does not equal the requesting principal's `memberId`. Linked design-system projects are single-writer: only the original creator is authorized to share or unshare the backing project.

Source

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

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);
            } catch (rollbackError) {
              // Remote rollback failed, so the project publication is still
              // Team-authoritative. Retry the local forward projection once;

View on GitHub (pinned to 5be4028344)

Solutions

  1. Have the original creator (the member whose id matches `binding.createdByWorkspaceMemberId`) perform the share/unshare.
  2. If ownership must change, update the binding's `createdByWorkspaceMemberId` through an authorized admin path first.
  3. Confirm the request principal (`scope.principal.memberId`) is being set correctly from the authenticated session.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before sharing/unsharing, confirm the requesting member is the creator:
const binding = getProjectBinding(projectId);
if (binding?.createdByWorkspaceMemberId !== scope.principal.memberId) {
  throw new Error('only the creator may share/unshare this design system');
}
await share(resourceId, scope);

Try / catch

import { TeamResourceShareForbiddenError } from '../collab/team-resource-share.js';

try {
  await linkedShare.share(resourceId, scope);
} catch (err) {
  if (err instanceof TeamResourceShareForbiddenError) {
    // surface 'only the creator can share this design system' to the user
  }
  throw err;
}

Prevention

When it happens

Trigger: A workspace member who is not the creator of the backing project binding attempts to share or unshare the linked design system. The preparer runs during `prepare()` and rejects before any hub mutation.

Common situations: A teammate (admin or otherwise) tries to unshare a design system that another member originally shared. Ownership was never transferred after a member left. A generic hub capability grants broad permissions, but the linked-project policy narrows unshare to the creator.

Related errors


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