Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[BUG] /zero/* WebSocket connections drop ~30s in, Documents panel never loads (self-hosted) #1745

Description

@nidzhat666

Bug Description

Every /zero/* WebSocket connection is closed abruptly by zero-cache roughly
30s after the handshake, in an infinite reconnect loop. The immediate effect
is that any UI backed by Zero (Documents list, notifications, etc.) never
finishes loading and stays stuck on its skeleton state, even though the
REST API works fine and documents are indexed successfully in the background.

Deployment Type

  • SurfSense Cloud (hosted version)
  • Self-hosted version

Steps to Reproduce

This reproduces with zero application state — no login, no cookies, no
real client — so it's not specific to any account/workspace/document:

curl -s -i -N --http1.1 --max-time 40 \
  -H "Connection: Upgrade" -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  "https://<your-domain>/zero/sync/v51/connect?clientID=test&clientGroupID=test&userID=test&baseCookie=&ts=0&lmid=0&wsid=test"

Response: 101 Switching Protocols, then the connection sits open for
exactly ~30 seconds and delivers this payload before the socket dies:

Error: Unexpected undefined value

In the real app: log in, open the Documents panel — it shows the skeleton
loading state and never resolves. DevTools console shows a WebSocket
reconnect loop against /zero/sync/v51/connect with ClientError: WebSocket connection closed abruptly, repeating with increasing backoff
forever.

Expected Behavior

The /zero/* WebSocket connection stays open and the Documents panel (and
other Zero-synced views) load normally.

Actual Behavior

zero-cache closes every connection ~30s in. Debug-level logs
(ZERO_LOG_LEVEL=debug) show the actual failure happens almost immediately
after initConnection, not at the 30s mark — the 30s is just ws's default
closeTimeout (node_modules/ws/lib/constants.js: CLOSE_TIMEOUT = 30000)
elapsing while it waits for a clean close handshake that never happens,
before hard-terminating the socket (hence code: 1006, wasClean: false
on the client side).

Relevant debug trace (single connection, clientID=onavlbmb2jcbfahafk):

{"level":"DEBUG",...,"message":"acquired lock for cvr"}
{"level":"DEBUG",...,"cmd":"initConnection","message":"mismatched wsID  B-5e8huSLCsTscTm-sCL0"}
{"level":"DEBUG",...,"message":"No auth maintenance wakeup scheduled"}
{"level":"WARN","worker":"dispatcher","message":"closing connection to client with error Error: Unexpected undefined value"}
{"level":"INFO",...,"message":"closing clientGroupID=..."}
{"level":"WARN",...,"errorBody":{"kind":"Internal","message":"shut down before initialization completed","origin":"zeroCache"},"name":"ProtocolError",...}

Traced Error: Unexpected undefined value to shared/src/must.js
(if (v == null) throw new Error(msg ?? \Unexpected ${v} value`)), and the close path to closeWithErrorinzero-cache/src/types/ws.js, called from the dispatcher's handoff error path in zero-cache/src/server/worker-dispatcher.js`.
Wasn't able to pin the exact call site further without unminified source —
happy to dig further if useful, or if there's a source-mapped build I can
point at instead.

Note: the mismatched wsID line in view-syncer.js (client?.wsID !== wsID → debug-log + early return) looks like a benign, expected no-op on
its own — flagging it only because it's the last view-syncer-side log line
before the dispatcher's crash, in case it's a useful breadcrumb.

Things I ruled out (self-hosted repro, all reproduce identically)

  • Reverse proxy: replaced bundled Caddy with nginx-proxy-manager,
    replicating the exact /zero/context → backend, /zero/* → zero-cache
    routing from docker/proxy/Caddyfile. Bypassed the proxy entirely (curl
    straight to the zero-cache container on the Docker network) — identical
    ~30s failure, so this isn't a proxy/header issue.
  • ZERO_NUM_SYNC_WORKERS: reproduces at both 4 (default) and 1.
  • Zero-cache version: reproduces identically on 1.6.0 (the pin in
    docker/docker-compose.yml), 1.6.2, and latest (=1.9.0 as of this
    writing, confirmed via Docker Hub digest match) — not a regression that
    got fixed upstream.
  • Stale/desynced Postgres state: docker compose down -v (wiped
    postgres_data and every other named volume), fresh alembic upgrade head on an empty database, brand-new user registration — same failure
    from the very first connection attempt.

Environment Information

  • Browser: Chrome (latest)
  • Operating System: self-hosted server: Ubuntu 24.04 (Docker host); client OS irrelevant, reproduces via bare curl
  • SurfSense Version: ghcr.io/modsetter/surfsense-backend:latest / surfsense-web:latest (pulled 2026-09-04)

Additional Environment Details (for Self-hosted only)

  • Database: pgvector/pgvector:pg17, fresh install, wal_level=logical per bundled postgresql.conf
  • Deployment Method: Docker Compose, adapted from docker/docker-compose.yml — services/env otherwise unchanged (ETL_SERVICE=DOCLING, EMBEDDING_MODEL=litellm://text-embedding-3-small, AUTH_TYPE=LOCAL, SANDBOX_ENABLED=FALSE)
  • Only structural deviations from the upstream compose: nginx-proxy-manager instead of bundled Caddy (see above — proven not the cause), and opensandbox-server/sandbox-image omitted entirely (unrelated services, no dependency on zero-cache)

Additional Context

This blocks real usage, not just a console warning — the Documents panel
(and presumably notifications, chat live-updates, and anything else backed
by Zero per zero-sync.mdx) never leaves its loading skeleton, since it's
sync-only with no REST fallback visible in the UI. Document upload/indexing
via Celery and the REST API works fine in the background; it's specifically
the Zero-synced read paths that never resolve.

Happy to run more diagnostics (e.g. capture the exact WS frames sent by the
real browser client, or bisect zero-cache versions further) — this repro is
fully scripted so turnaround is fast.

Checklist

  • I have searched existing issues to ensure this is not a duplicate
  • I have provided all the required information above
  • I have added appropriate labels (bug, deployment type)

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions