{"record":{"id":"17bbd772c7f20134","repo":"hcengineering/platform","slug":"account-status-workspacenotfound","errorCode":"account.status.WorkspaceNotFound","errorMessage":"WorkspaceNotFound","messagePattern":"WorkspaceNotFound","errorType":"error_code","errorClass":"PlatformError","httpStatus":null,"severity":"error","filePath":"server/account/src/operations.ts","lineNumber":3435,"sourceCode":"\n  return subscription\n}\n\nexport async function batchAssignWorkspacePermission (\n  ctx: MeasureContext,\n  db: AccountDB,\n  branding: Branding | null,\n  token: string,\n  params: {\n    accountIds: AccountUuid[]\n    permission: string\n  }\n): Promise<void> {\n  const { accountIds, permission } = params\n  const { account, workspace } = decodeTokenVerbose(ctx, token)\n\n  if (workspace === null) {\n    throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspaceUuid: workspace }))\n  }\n\n  const accRole = account === systemAccountUuid ? AccountRole.Owner : await db.getWorkspaceRole(account, workspace)\n  if (accRole == null || getRolePower(accRole) < getRolePower(AccountRole.Maintainer)) {\n    throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))\n  }\n\n  await db.batchAssignWorkspacePermission(workspace, accountIds, permission)\n}\n\nexport async function batchRevokeWorkspacePermission (\n  ctx: MeasureContext,\n  db: AccountDB,\n  branding: Branding | null,\n  token: string,\n  params: {\n    accountIds: AccountUuid[]\n    permission: string","sourceCodeStart":3417,"sourceCodeEnd":3453,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/server/account/src/operations.ts#L3417-L3453","documentation":"WorkspaceNotFound is thrown by batchAssignWorkspacePermission when decodeTokenVerbose returns workspace === null, i.e. the provided token is not bound to any workspace. The operation requires a workspace-scoped token to know which workspace's permissions to assign.","triggerScenarios":"Calling batchAssignWorkspacePermission with a personal/account-level token that carries no workspace claim (workspace resolves to null).","commonSituations":"Using a login token instead of a workspace token; workspace was deleted so the token's workspace claim is gone; constructing a token manually without the workspace field; env/region mixups yielding a token from a non-workspace endpoint.","solutions":["Obtain a workspace-scoped token (e.g. login with workspace selection or use selectWorkspace) before calling.","Check that the target workspace still exists and the token was issued for it.","Re-issue/refresh the token if it predates workspace deletion or migration."],"exampleFix":"// before\nawait accountClient.batchAssignWorkspacePermission(loginToken, { accountIds, permission })\n// after\nconst wsToken = await accountClient.selectWorkspace(loginToken, workspaceUuid)\nawait accountClient.batchAssignWorkspacePermission(wsToken, { accountIds, permission })","handlingStrategy":"try-catch","validationCode":"const role = await getMyWorkspaceRole(workspaceUuid)\nif (role === null || getRolePower(role) < getRolePower(AccountRole.Maintainer)) {\n  throw new Error('Maintainer role required to read subscription')\n}","typeGuard":"function hasMaintainerRole(role: AccountRole | null): boolean {\n  return role !== null && getRolePower(role) >= getRolePower(AccountRole.Maintainer)\n}","tryCatchPattern":"try {\n  const subscription = await client.fetchSubscription(token)\n} catch (err) {\n  if (isPlatformError(err) && err.status.code === account.status.Forbidden) {\n    // surface 'requires Maintainer role' UI state or re-auth as admin\n  }\n  throw err\n}","preventionTips":["Verify the caller's workspace role before making admin-scoped subscription calls.","Keep a separate maintainer/owner token for billing operations.","Assert the subscription's workspaceUuid matches the token's workspace before calling."],"tags":["authorization","workspace","token","not-found"],"backgroundTag":"missing-workspace-in-token","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}