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') 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.
Source
Thrown at apps/daemon/src/collab/team-resource-share.ts:209
const adapter: ResourcePublishAdapter = createVelaCliResourceAdapter({
resolveProjectDir: options.resolveDir,
resourceIdFor,
kind: options.kind,
// Every operation below already requires a request-verified Team scope.
// Re-reading the daemon's ambient Workspace here would reintroduce the
// cross-tab switch race this service boundary exists to prevent.
hasTeamIdentity: () => true,
...(options.describeResource ? { describeProject: options.describeResource } : {}),
...(options.run ? { run: options.run } : {}),
});
return {
async share(resourceId, scope) {
// Permission gate: only a member who can manage shared resources may
// promote one to the team. The route supplied this bit from the
// authoritative directory, never from caller-controlled headers.
if (!scope.canShare) throw new TeamResourceShareForbiddenError();
const { principal } = scope;
const result = await adapter.publish({
projectId: resourceId,
principal,
reason: 'share',
});
if (result) sharedFor(principal.teamId).add(resourceId);
return result;
},
async unshare(resourceId, scope) {
const { principal } = scope;
const sharedResource = (await this.sharedResources(scope)).find((resource) => resource.id === resourceId);
if (sharedResource && !sharedResource.canUnshare) {
throw new TeamResourceShareForbiddenError();
}
await adapter.unpublish?.({ projectId: resourceId, principal });
sharedFor(principal.teamId).delete(resourceId);
return true;View on GitHub (pinned to 5be4028344)
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.
Defensive patterns
Strategy: try-catch
Validate before calling
// Resolve scope first; if the member cannot share, do not call share().
const scope = teamResourceRequestScopeFromContext(context);
if (!scope?.canShare) {
throw new Error('current member lacks share permission');
}
await service.share(resourceId, scope); Type guard
import { TeamResourceShareForbiddenError } from './team-resource-share.js';
// (err instanceof TeamResourceShareForbiddenError) is the guard Try / catch
try {
await service.share(resourceId, scope);
} catch (err) {
if (err instanceof TeamResourceShareForbiddenError) {
// return 403 'you do not have permission to share to this team'
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- WORKSPACE_PROJECT_PERMISSION_DENIED
- Project ${projectId} not found
- authoritative Team resource listing is unavailable
- WORKSPACE_RESOURCE_AUTHORITY_UNAVAILABLE
- design system backing project is unavailable
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/eda3f61a8d86c694.
Report an issue: GitHub.