Skip to content

Community edition: a user with zero workspaces has no UI path to create one — showCreateCard hides the only entry point into /channels/create #1226

Description

@rawdaymx

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

  1. 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).
  2. Load /. WorkspacesList computes showCreateCard = !isCommunity()false.
  3. hasWorkspaces is also false (zero workspaces), so the ternary at line ~264 renders the
    <p>{t("home.noWorkspaces")}</p> branch — no <CreateWorkspaceCard>.
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions