multica-ai/multica · error
workspace not found
Error message
workspace not found
What it means
HTTP 404 from the realtime WebSocket upgrade handler when no workspace_id query param was supplied, a workspace_slug was given instead, and the slug resolver (e.g. a DB lookup mapping slug → workspace UUID) returned an error. The slug does not resolve to any workspace: it is unknown, mistyped, or the lookup itself failed; the handler collapses both to a single 404 'workspace not found'.
Source
Thrown at server/internal/realtime/hub.go:774
return false
}
return true
}
func writeWSAuthErrorAndClose(conn *websocket.Conn, payload []byte, attrs ...any) {
writeWSAuthFrame(conn, payload, "auth_error", attrs...)
conn.Close()
}
// HandleWebSocket upgrades an HTTP connection to WebSocket with cookie or
// first-message auth.
func HandleWebSocket(hub *Hub, mc MembershipChecker, pr PATResolver, resolveSlug SlugResolver, w http.ResponseWriter, r *http.Request) {
workspaceID := r.URL.Query().Get("workspace_id")
if workspaceID == "" {
if slug := r.URL.Query().Get("workspace_slug"); slug != "" && resolveSlug != nil {
resolved, err := resolveSlug(r.Context(), slug)
if err != nil {
http.Error(w, `{"error":"workspace not found"}`, http.StatusNotFound)
return
}
workspaceID = resolved
}
}
if workspaceID == "" {
http.Error(w, `{"error":"workspace_id or workspace_slug required"}`, http.StatusBadRequest)
return
}
var userID string
if cookie, err := r.Cookie(auth.AuthCookieName); err == nil && cookie.Value != "" {
uid, errMsg := authenticateToken(cookie.Value, pr, r.Context())
if errMsg != "" {
http.Error(w, errMsg, http.StatusUnauthorized)
return
}
if !mc.IsMember(r.Context(), uid, workspaceID) {View on GitHub (pinned to 2c0912b6ec)
Solutions
- Verify the slug against the workspace list API (GET /api/workspaces) and correct it.
- Prefer workspace_id (the UUID) when known — it skips slug resolution entirely.
- If the workspace was renamed, refresh the page/client so it picks up the new slug or the ID.
- If the resolver logs an error, check DB connectivity/permissions on the server.
Example fix
// before: connect by a possibly stale slug
new WebSocket("ws://host/ws?workspace_slug=" + slug)
// after: resolve the id once and connect by id
const ws = await api.workspaces.list()
const id = ws.find(w => w.slug === slug)?.id
if (id) new WebSocket("ws://host/ws?workspace_id=" + id) Defensive patterns
Strategy: validation
Validate before calling
// resolve slug → id via the REST API before opening the socket
wsList, err := api.ListWorkspaces(ctx)
if err != nil { return err }
var id string
for _, w := range wsList { if w.Slug == slug { id = w.ID; break } }
if id == "" { return fmt.Errorf("unknown workspace slug: %s", slug) } Type guard
func hasWorkspaceTarget(idParam, slug string) bool { return idParam != "" || slug != "" } Try / catch
conn, resp, err := dialer.Dial(url, nil)
if err != nil && resp != nil && resp.StatusCode == 404 {
// slug stale: re-resolve via REST, then reconnect with workspace_id once
} Prevention
- Prefer workspace_id (UUID) over slug for machine clients.
- Re-resolve slugs after workspace rename/delete events.
- Handle 404 on socket connect by refreshing workspace metadata, not by looping.
When it happens
Trigger: GET /ws?workspace_slug=does-not-exist (typo, deleted workspace, wrong organization scope); slug resolver DB error; slug containing URL-unsafe characters that mangle the lookup.
Common situations: Workspace renamed or deleted between page load and socket connect; user pastes an old invite link with a stale slug; per-workspace DB routing looking in the wrong shard.
Related errors
- workspace_id or workspace_slug required
- Invalid desktop runtime config JSON: ${err instanceof Error
- repo is not configured for this workspace
- config must be a JSON object
- config must not be empty
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/581a59fca0492901.
Report an issue: GitHub.