{"record":{"id":"eda3f61a8d86c694","repo":"nexu-io/open-design","slug":"workspace-resource-share-denied","errorCode":null,"errorMessage":"workspace_resource_share_denied","messagePattern":"workspace_resource_share_denied","errorType":"exception","errorClass":"TeamResourceShareForbiddenError","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/collab/team-resource-share.ts","lineNumber":209,"sourceCode":"\n  const adapter: ResourcePublishAdapter = createVelaCliResourceAdapter({\n    resolveProjectDir: options.resolveDir,\n    resourceIdFor,\n    kind: options.kind,\n    // Every operation below already requires a request-verified Team scope.\n    // Re-reading the daemon's ambient Workspace here would reintroduce the\n    // cross-tab switch race this service boundary exists to prevent.\n    hasTeamIdentity: () => true,\n    ...(options.describeResource ? { describeProject: options.describeResource } : {}),\n    ...(options.run ? { run: options.run } : {}),\n  });\n\n  return {\n    async share(resourceId, scope) {\n      // Permission gate: only a member who can manage shared resources may\n      // promote one to the team. The route supplied this bit from the\n      // authoritative directory, never from caller-controlled headers.\n      if (!scope.canShare) throw new TeamResourceShareForbiddenError();\n      const { principal } = scope;\n      const result = await adapter.publish({\n        projectId: resourceId,\n        principal,\n        reason: 'share',\n      });\n      if (result) sharedFor(principal.teamId).add(resourceId);\n      return result;\n    },\n    async unshare(resourceId, scope) {\n      const { principal } = scope;\n      const sharedResource = (await this.sharedResources(scope)).find((resource) => resource.id === resourceId);\n      if (sharedResource && !sharedResource.canUnshare) {\n        throw new TeamResourceShareForbiddenError();\n      }\n      await adapter.unpublish?.({ projectId: resourceId, principal });\n      sharedFor(principal.teamId).delete(resourceId);\n      return true;","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/collab/team-resource-share.ts#L191-L227","documentation":"Thrown as TeamResourceShareForbiddenError (message 'workspace_resource_share_denied') by share() when scope.canShare is false. The TeamResourceRequestScope.canShare bit is derived from the authoritative membership directory — true only when the member has canManageSharedResources or canShareProjects permission. The gate is enforced server-side from request-verified authority, never from caller-controlled headers, so a client cannot elevate itself.","triggerScenarios":"A member with a viewer/read-only role (no canManageSharedResources, no canShareProjects) calling the share endpoint, or a member whose directory entry lacks the share permission bits attempting to promote a resource to the team.","commonSituations":"A non-owner teammate trying to share a design system/plugin/skill they made, a role downgrade that removed share rights, or stale UI state after a permissions change.","solutions":["Use an account/role that has canManageSharedResources or canShareProjects on the target team.","Have a team owner/admin grant the share permission, or share on the member's behalf from an authorized account.","Refresh the workspace directory so the canShare bit reflects the current role before retrying."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Resolve scope first; if the member cannot share, do not call share().\nconst scope = teamResourceRequestScopeFromContext(context);\nif (!scope?.canShare) {\n  throw new Error('current member lacks share permission');\n}\nawait service.share(resourceId, scope);","typeGuard":"import { TeamResourceShareForbiddenError } from './team-resource-share.js';\n// (err instanceof TeamResourceShareForbiddenError) is the guard","tryCatchPattern":"try {\n  await service.share(resourceId, scope);\n} catch (err) {\n  if (err instanceof TeamResourceShareForbiddenError) {\n    // return 403 'you do not have permission to share to this team'\n  }\n  throw err;\n}","preventionTips":["Resolve TeamResourceRequestScope from the authoritative directory and check canShare before calling share().","Refresh membership/permissions before retrying after a role change.","Surface 403 to the client; do not silently no-op a share attempt."],"tags":["collab","team-resources","permissions","authorization","share"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}