{"record":{"id":"4959e7f5be939430","repo":"nexu-io/open-design","slug":"workspace-project-permission-denied-4959e7","errorCode":"WORKSPACE_PROJECT_PERMISSION_DENIED","errorMessage":"workspace project creation is not allowed","messagePattern":"workspace project creation is not allowed","errorType":"exception","errorClass":"CreatedProjectWorkspaceResolutionError","httpStatus":403,"severity":"error","filePath":"apps/daemon/src/collab/created-project-workspace.ts","lineNumber":189,"sourceCode":"    this.name = 'CreatedProjectWorkspaceResolutionError';\n    this.status = error.status;\n    this.code = error.code;\n    if (error.retryable) this.retryable = true;\n  }\n}\n\n/**\n * Resolve an exact creation scope. Headerless legacy requests remain unbound.\n * Once either identity field is asserted, any incomplete, removed, denied, or\n * unavailable authority fails closed; it never degrades to ambient/current or\n * silently creates an unbound project.\n */\nexport async function createdProjectWorkspaceHome(\n  req: unknown,\n  fetchWorkspaceDirectory?: () => Promise<WorkspaceDirectoryFetchResult>,\n): Promise<WorkspaceResourceContext | null> {\n  const authorized = await authorizeCreatedProjectWorkspace(req, fetchWorkspaceDirectory);\n  if (!authorized.ok) throw new CreatedProjectWorkspaceResolutionError(authorized);\n  return authorized.context;\n}\n\n/**\n * A `createdProjectWorkspaceHome` bound to one daemon's authorities, so a route\n * module takes a single dep instead of re-threading three.\n */\nexport type CreatedProjectWorkspaceResolver = (\n  req: unknown,\n) => Promise<WorkspaceResourceContext | null>;\n\nexport function createCreatedProjectWorkspaceResolver(deps: {\n  fetchWorkspaceDirectory?: () => Promise<WorkspaceDirectoryFetchResult>;\n}): CreatedProjectWorkspaceResolver {\n  return (req) =>\n    createdProjectWorkspaceHome(\n      req,\n      deps.fetchWorkspaceDirectory,","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/collab/created-project-workspace.ts#L171-L207","documentation":"Thrown by createdProjectWorkspaceHome as a CreatedProjectWorkspaceResolutionError carrying code WORKSPACE_PROJECT_PERMISSION_DENIED and HTTP status 403. It fires after authorizeCreatedProjectWorkspace resolves to a denied state: the requesting member is not 'active', lacks canWriteSyncedFiles, the workspace resource is locked, or the (workspaceId, workspaceMemberId) pair is not present in the authoritative membership directory. The resolver intentionally fails closed rather than silently creating an unbound project once any workspace identity is asserted.","triggerScenarios":"A project-creation request carrying workspace/member headers from a user whose membership was revoked or suspended, whose role cannot write synced files, whose resource is locked, or who is not in the directory at all. Also when the directory fetch returns a membership that fails the active+write+unlocked re-check.","commonSituations":"Membership revoked mid-session (token still cached locally), a read-only/viewer role trying to create a synced project, a locked workspace during maintenance, or stale headers after a workspace switch.","solutions":["Verify the user's membership and role in the target team (active member with write permission).","Re-authenticate / refresh the workspace directory so headers reflect current membership.","Retry when the workspace is no longer locked, if the lock was the cause.","If a personal (unbound) project is acceptable, create it without workspace identity headers — headerless legacy requests remain unbound by design."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-check membership before creating a bound project.\nconst resolution = await authorizeCreatedProjectWorkspace(req, fetchWorkspaceDirectory);\nif (!resolution.ok && resolution.code === 'WORKSPACE_PROJECT_PERMISSION_DENIED') {\n  // surface 403 to the user, or fall back to a headerless personal create\n}","typeGuard":"function isPermissionDenied(err: unknown): boolean {\n  return err instanceof Error && (err as { code?: string }).code === 'WORKSPACE_PROJECT_PERMISSION_DENIED';\n}","tryCatchPattern":"import { CreatedProjectWorkspaceResolutionError } from './created-project-workspace.js';\ntry {\n  await createdProjectWorkspaceHome(req, fetchWorkspaceDirectory);\n} catch (err) {\n  if (err instanceof CreatedProjectWorkspaceResolutionError && err.code === 'WORKSPACE_PROJECT_PERMISSION_DENIED') {\n    // return 403 to the client, or retry without workspace headers for a personal project\n  }\n  throw err;\n}","preventionTips":["Refresh the workspace directory before creation so membership/role is current.","Only assert workspace headers when you genuinely want a bound (team) project.","For personal projects, omit workspace identity headers — headerless requests stay unbound by design."],"tags":["workspace","collab","permissions","authorization","project-creation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}