Summary
On the community edition, WorkspacesList sets showCreateCard = !isCommunity()
(workspaces-list.tsx:200). When a user has zero workspaces AND is on community, the page's
conditional (hasWorkspaces || showCreateCard) is false || false, so it renders nothing but a
static <p>{t("home.noWorkspaces")}</p> — no button, no link, nothing clickable anywhere on the
page or its sidebar (AccountRail only links to /admin, /manage, and the billing/redeem
portal — none of which create a workspace).
The only path into createFirstWorkspace is the create-workspace card, which links to
/channels/create. Hiding it for community removes the only way a fresh community sign-up can
ever get their first workspace — and onUserCreated is a no-op outside isCloud(), so no
workspace is auto-provisioned at sign-up either. A control confirms this is specific to the
showCreateCard gate: the same page, same zero-workspace state, with isCloud() instead of
isCommunity(), shows the card and works fine.
Environment
Static defect — read against upstream/main at commit 62e9fe323.
| File |
Line(s) |
apps/builder/src/features/workspaces/components/workspaces-list.tsx |
200, 209-270 |
apps/builder/src/app/page.tsx |
24-100 |
apps/builder/src/lib/workspace/create-first-workspace.ts |
55-66 |
apps/builder/src/lib/auth/on-user-created.ts |
23-25 |
How to see it
- Run the community edition with a fresh user who has zero workspaces (
onUserCreated is a
no-op there — no workspace gets provisioned at sign-up).
- Load
/. WorkspacesList computes showCreateCard = !isCommunity() → false.
hasWorkspaces is also false (zero workspaces), so the ternary at line ~264 renders the
<p>{t("home.noWorkspaces")}</p> branch — no <CreateWorkspaceCard>.
- Nothing else on the page (
AccountRail, the header) links anywhere that reaches
/channels/create, the only route that calls createFirstWorkspace.
Expected: a community user, like a cloud user, has some way to create their first workspace.
Actual: the page renders a static sentence and nothing else — a permanent dead end reachable
purely by signing up on community with no workspace yet.
Where it comes from
// apps/builder/src/features/workspaces/components/workspaces-list.tsx:196-200
const t = await getTranslations()
const createLabel = t("actions.createFeature", {
feature: t("fields.workspace.label"),
})
const showCreateCard = !isCommunity()
// same file, ~264-270
{hasWorkspaces || showCreateCard ? (
<ul>...</ul>
) : (
<p className="text-muted-foreground text-sm">{t("home.noWorkspaces")}</p>
)}
Why we think this is a defect rather than the intended design
We looked for a deliberate reason before writing this up, and found something worth naming
directly rather than omitting: showCreateCard = !isCommunity || workspaces.length === 0 — the
exact condition we're proposing below — was the original code, added in PR #359. PR #405
(titled "bug: invite workspace member", empty body) removed the || workspaces.length === 0
clause, leaving !isCommunity alone. That diff is the entire connection between #405 and this
behavior.
We looked at what else #405 touches before treating that removal as either "on purpose" or "an
accident": 17 files, almost all of them an unrelated restructuring of enterprise routes (deletes
(enterprise)/manage/page.tsx and (enterprise)/space/[workspaceId]/layout.tsx, renames several
routes under manage/), plus the actual invite-member fix the title describes (switching
invite-workspace-member.action.ts from randomString to a new SymbolicSnowflakeIDs). None of
the other 16 files touch workspace-creation or community-specific logic. We can't prove the
showCreateCard line was accidental — the PR has no body and we didn't find an originating issue
— but we also found no PR, comment, or test anywhere that treats the zero-workspace case as a
deliberate choice. We're naming #405 here so a maintainer with more context can settle it either
way, rather than asserting an absence of intent we can't fully back up.
Separately, PR #609 (unrelated to this specific line) states the project's general stance:
"Self-hosted community/enterprise installs keep every feature free — no quota gating, no
upgrade prompts." If that's still the guiding policy, hiding the only path to a first workspace
runs against it regardless of how #405 is resolved.
Suggested fix
Only hide the card once the community install already has a workspace, not before:
- const showCreateCard = !isCommunity()
+ // Community hides the card once a workspace exists (self-hosted installs
+ // are meant to stay single-workspace), but a user with ZERO workspaces has
+ // no other path to `/channels/create` anywhere on this page — hiding it
+ // here leaves them stuck on a static "no workspaces" message forever.
+ const showCreateCard = !isCommunity() || workspaces.length === 0
Full patch and a new test (workspaces-list.community-zero.test.tsx, 2 cases: card shows with
zero workspaces even on community, card still hides once one exists) are attached; both ran
red→green locally against this exact commit.
What we did not verify
- We did not check whether community is meant to support more than one workspace at all — our
fix preserves whatever restriction was intended for the second workspace onward, and only
changes behavior for the zero-workspace case.
- We did not run the full builder test suite locally (resource constraints on our machine); we
ran the targeted test file plus tsc --noEmit and the project's own lint (ultracite check)
on the two changed files.
Related issues — none of these describes it
Searched "community no workspaces", "showCreateCard", "isCommunity workspace", and
"self-hosted create workspace" — no results for any. A control search for "getUserData commentAnchor" (expected to return our own issue #1186) did return it, confirming the search
mechanism works and the empty results above are a true absence.
Summary
On the community edition,
WorkspacesListsetsshowCreateCard = !isCommunity()(
workspaces-list.tsx:200). When a user has zero workspaces AND is on community, the page'sconditional (
hasWorkspaces || showCreateCard) isfalse || false, so it renders nothing but astatic
<p>{t("home.noWorkspaces")}</p>— no button, no link, nothing clickable anywhere on thepage or its sidebar (
AccountRailonly links to/admin,/manage, and the billing/redeemportal — none of which create a workspace).
The only path into
createFirstWorkspaceis the create-workspace card, which links to/channels/create. Hiding it for community removes the only way a fresh community sign-up canever get their first workspace — and
onUserCreatedis a no-op outsideisCloud(), so noworkspace is auto-provisioned at sign-up either. A control confirms this is specific to the
showCreateCardgate: the same page, same zero-workspace state, withisCloud()instead ofisCommunity(), shows the card and works fine.Environment
Static defect — read against
upstream/mainat commit62e9fe323.apps/builder/src/features/workspaces/components/workspaces-list.tsxapps/builder/src/app/page.tsxapps/builder/src/lib/workspace/create-first-workspace.tsapps/builder/src/lib/auth/on-user-created.tsHow to see it
onUserCreatedis ano-op there — no workspace gets provisioned at sign-up).
/.WorkspacesListcomputesshowCreateCard = !isCommunity()→false.hasWorkspacesis alsofalse(zero workspaces), so the ternary at line ~264 renders the<p>{t("home.noWorkspaces")}</p>branch — no<CreateWorkspaceCard>.AccountRail, the header) links anywhere that reaches/channels/create, the only route that callscreateFirstWorkspace.Expected: a community user, like a cloud user, has some way to create their first workspace.
Actual: the page renders a static sentence and nothing else — a permanent dead end reachable
purely by signing up on community with no workspace yet.
Where it comes from
Why we think this is a defect rather than the intended design
We looked for a deliberate reason before writing this up, and found something worth naming
directly rather than omitting:
showCreateCard = !isCommunity || workspaces.length === 0— theexact condition we're proposing below — was the original code, added in PR #359. PR #405
(titled "bug: invite workspace member", empty body) removed the
|| workspaces.length === 0clause, leaving
!isCommunityalone. That diff is the entire connection between #405 and thisbehavior.
We looked at what else #405 touches before treating that removal as either "on purpose" or "an
accident": 17 files, almost all of them an unrelated restructuring of enterprise routes (deletes
(enterprise)/manage/page.tsxand(enterprise)/space/[workspaceId]/layout.tsx, renames severalroutes under
manage/), plus the actual invite-member fix the title describes (switchinginvite-workspace-member.action.tsfromrandomStringto a newSymbolicSnowflakeIDs). None ofthe other 16 files touch workspace-creation or community-specific logic. We can't prove the
showCreateCardline was accidental — the PR has no body and we didn't find an originating issue— but we also found no PR, comment, or test anywhere that treats the zero-workspace case as a
deliberate choice. We're naming #405 here so a maintainer with more context can settle it either
way, rather than asserting an absence of intent we can't fully back up.
Separately, PR #609 (unrelated to this specific line) states the project's general stance:
"Self-hosted community/enterprise installs keep every feature free — no quota gating, no
upgrade prompts." If that's still the guiding policy, hiding the only path to a first workspace
runs against it regardless of how #405 is resolved.
Suggested fix
Only hide the card once the community install already has a workspace, not before:
Full patch and a new test (
workspaces-list.community-zero.test.tsx, 2 cases: card shows withzero workspaces even on community, card still hides once one exists) are attached; both ran
red→green locally against this exact commit.
What we did not verify
fix preserves whatever restriction was intended for the second workspace onward, and only
changes behavior for the zero-workspace case.
ran the targeted test file plus
tsc --noEmitand the project's own lint (ultracite check)on the two changed files.
Related issues — none of these describes it
Searched
"community no workspaces","showCreateCard","isCommunity workspace", and"self-hosted create workspace"— no results for any. A control search for"getUserData commentAnchor"(expected to return our own issue #1186) did return it, confirming the searchmechanism works and the empty results above are a true absence.