multica-ai/multica · error
runtime_ids or user identity required
Error message
runtime_ids or user identity required
What it means
HTTP 400 from the daemon WebSocket hub's HandleWebSocket when the resolved client identity carries neither runtime IDs nor a user ID. The hub routes messages per runtime and per user, so a completely anonymous identity cannot be attached to any topic and is rejected before the connection is upgraded.
Source
Thrown at server/internal/daemonws/hub.go:283
return
}
h.kindMu.Lock()
h.kindRecorder = rec
h.kindMu.Unlock()
}
func (h *Hub) messageKindRecorder() MessageKindRecorder {
if h == nil {
return nil
}
h.kindMu.RLock()
defer h.kindMu.RUnlock()
return h.kindRecorder
}
func (h *Hub) HandleWebSocket(w http.ResponseWriter, r *http.Request, identity ClientIdentity) {
if len(identity.RuntimeIDs) == 0 && identity.UserID == "" {
http.Error(w, `{"error":"runtime_ids or user identity required"}`, http.StatusBadRequest)
return
}
conn, err := h.upgrader.Upgrade(w, r, nil)
if err != nil {
slog.Error("daemon websocket upgrade failed", "error", err)
return
}
runtimes := make(map[string]struct{}, len(identity.RuntimeIDs))
for _, runtimeID := range identity.RuntimeIDs {
if runtimeID != "" {
runtimes[runtimeID] = struct{}{}
}
}
c := &client{
hub: h,
conn: conn,View on GitHub (pinned to 2c0912b6ec)
Solutions
- Ensure the client authenticates so the hub resolves a UserID, or supplies at least one non-empty runtime ID.
- Check any proxy between client and daemon preserves the headers/cookies/params the identity is derived from.
- Filter empty strings out of runtime ID lists client-side before connecting.
- Verify the daemon auth middleware is enabled — an unauthenticated request yields an empty identity.
Example fix
// before: connect with no identity
dial("ws://daemon/ws") // 400 runtime_ids or user identity required
// after: connect authenticated (cookie/PAT) or with runtime ids
dial("ws://daemon/ws", {headers: {"Authorization": "Bearer " + pat}})
// or
ids := []string{}
for _, id := range runtimeIDs { if id != "" { ids = append(ids, id) } }
dial("ws://daemon/ws?runtime_id=" + strings.Join(ids, ",")) Defensive patterns
Strategy: validation
Validate before calling
func validIdentity(identity ClientIdentity) bool {
if identity.UserID != "" { return true }
for _, id := range identity.RuntimeIDs { if id != "" { return true } }
return false
}
if !validIdentity(id) { return errors.New("refusing to dial: no identity") } Type guard
func hasIdentity(id ClientIdentity) bool {
if strings.TrimSpace(id.UserID) != "" { return true }
for _, r := range id.RuntimeIDs { if strings.TrimSpace(r) != "" { return true } }
return false
} Try / catch
conn, resp, err := dialer.Dial(wsURL, headers)
if err != nil && resp != nil && resp.StatusCode == 400 {
// identity rejected: re-authenticate, then re-dial once
} Prevention
- Filter empty strings from runtime ID lists before connecting.
- Verify proxies preserve identity headers.
- Fail fast client-side when no identity is available instead of dialing.
When it happens
Trigger: Opening a WebSocket to the daemon endpoint with an identity whose RuntimeIDs list is empty (or all blank strings) and whose UserID is empty — e.g. a misconfigured proxy that strips the identity headers/params the hub derives ClientIdentity from, or a client that never authenticates.
Common situations: Auth middleware disabled or bypassed in local dev; a reverse proxy dropping the identity headers; a new client integration that forgot to send runtime identification; runtime IDs sent as empty strings.
Related errors
- Invalid desktop runtime config JSON: ${err instanceof Error
- agent execution context requires MULTICA_TOKEN to be a task-
- %s is not available inside a daemon-managed task%s
- %s is not available inside a daemon-managed task
- workspace_id or workspace_slug required
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/35ff42e31e6c0078.
Report an issue: GitHub.