{"record":{"id":"2e642d7a82e16eda","repo":"windmill-labs/windmill","slug":"community-edition-is-limited-to-ce-max-non-admin","errorCode":null,"errorMessage":"Community edition is limited to ${CE_MAX_NON_ADMIN_WORKSPACES + 1} workspaces — archive a workspace or pick one to run in","messagePattern":"Community edition is limited to (.+?) workspaces — archive a workspace or pick one to run in","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/sessions/sessionState.svelte.ts","lineNumber":913,"sourceCode":"\tif (s.workspace_id) return s.workspace_id\n\t// A commit is a send: the record must be durable regardless of prior touches\n\t// (a draft sent without ever being touched is still transient here).\n\tif (s.transient) delete s.transient\n\n\tif (s.pending_fork) {\n\t\tconst fork = s.pending_fork\n\t\t// Defense-in-depth against a stale pending_fork (e.g. staged before\n\t\t// another workspace was created, or any future entry point): a fork is a\n\t\t// new workspace, and community edition caps the number of non-'admins'\n\t\t// workspaces (backend _check_nb_of_workspaces). An enterprise license\n\t\t// lifts the cap. Block the commit with an explicit error rather than\n\t\t// letting materializeFork hit a backend rejection. Keep the pending fork\n\t\t// set so the block persists until the user picks a non-fork workspace\n\t\t// (setSessionPendingWorkspace clears it).\n\t\tconst CE_MAX_NON_ADMIN_WORKSPACES = 2\n\t\tconst nonAdminWorkspaceCount = get(userWorkspaces).filter((w) => w.id !== 'admins').length\n\t\tif (!get(enterpriseLicense) && nonAdminWorkspaceCount >= CE_MAX_NON_ADMIN_WORKSPACES) {\n\t\t\tthrow new Error(\n\t\t\t\t`Community edition is limited to ${CE_MAX_NON_ADMIN_WORKSPACES + 1} workspaces — archive a workspace or pick one to run in`\n\t\t\t)\n\t\t}\n\t\tconst newId = await materializeFork(fork)\n\t\tif (!newId) {\n\t\t\t// Real failure (not a recovered duplicate). Drop the pending\n\t\t\t// fork so the session falls through to the workspace-pick\n\t\t\t// fallback on the next call and the unavailable-banner UX\n\t\t\t// can take over instead of looping on the same broken intent.\n\t\t\ts.pending_fork = undefined\n\t\t\tvoid putSession(s)\n\t\t\treturn undefined\n\t\t}\n\t\ts.workspace_id = newId\n\t\ts.pending_fork = undefined\n\t\ts.pending_workspace_id = undefined\n\t\ts.workspace_root_id = workspaceRootId(newId, get(userWorkspaces)) ?? newId\n\t\t// The draft prompt has been consumed as the first message.","sourceCodeStart":895,"sourceCodeEnd":931,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/sessions/sessionState.svelte.ts#L895-L931","documentation":"sessionState.svelte.ts enforces the Community Edition limit of 2 non-admin workspaces (CE_MAX_NON_ADMIN_WORKSPACES = 2, +1 counting the admins workspace). When materializing a session fork would create a workspace beyond that limit on a non-EE instance, this error is thrown before materializeFork is called. The pending fork is intentionally kept set so the block persists until the user picks a non-fork workspace (setSessionPendingWorkspace clears it).","triggerScenarios":"On a Community Edition instance (no enterpriseLicense), attempting to create/commit a session fork when `get(userWorkspaces)` already contains CE_MAX_NON_ADMIN_WORKSPACES (2) workspaces other than 'admins'.","commonSituations":"Running the free CE edition in a setup with several workspaces and trying to fork a session into a new workspace; hitting this during AI session runs that auto-fork.","solutions":["Archive an existing workspace to free a slot, then retry the fork.","Pick an existing (non-fork) workspace for the session instead of forking into a new one — clearing the pending workspace unblocks the flow.","Upgrade to an Enterprise license to lift the workspace limit.","For local/dev testing, remove or archive unused non-admin workspaces before forking."],"exampleFix":"// before\nconst newId = await materializeFork(fork) // throws on CE limit\n// after\nconst canFork = get(enterpriseLicense) ||\n  get(userWorkspaces).filter((w) => w.id !== 'admins').length < CE_MAX_NON_ADMIN_WORKSPACES\nif (canFork) {\n  const newId = await materializeFork(fork)\n} else {\n  // prompt the user to pick an existing workspace or archive one\n}","handlingStrategy":"validation","validationCode":"import { get } from 'svelte/store'\nimport { userWorkspaces, enterpriseLicense } from '$lib/stores'\nfunction canCreateWorkspace(): boolean {\n  if (get(enterpriseLicense)) return true\n  return get(userWorkspaces).filter((w) => w.id !== 'admins').length < 2\n}","typeGuard":"function hasEnterpriseLicense(v: unknown): v is EnterpriseLicense {\n  return v !== null && v !== undefined\n}","tryCatchPattern":"try {\n  const id = await materializeFork(fork)\n} catch (e) {\n  if (String(e).includes('Community edition is limited')) {\n    toast.error('CE workspace limit reached — archive a workspace or pick an existing one')\n    // keep pending fork set so the block persists by design\n    return\n  }\n  throw e\n}","preventionTips":["Check the workspace count before initiating a fork on CE instances","Archive unused workspaces proactively","Pick an existing workspace (clearing the pending fork) instead of forking when near the limit","Upgrade to EE if multi-workspace forks are a regular workflow"],"tags":["licensing","workspace","community-edition"],"backgroundTag":"ce-workspace-limit-reached","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}