# Connection troubleshooting

When collaboration "doesn't connect", it is almost always one of a handful of
things: TLS/proxy setup, authentication, or an idle-timeout closing the
WebSocket. This page maps the symptoms to causes, keyed to the server's actual
error codes.

## First checks

1. **Is the server up?** The health endpoint needs no auth:

    ```bash
    curl https://collab.example.com/collab/health
    # → {"success":true,"protocol":1}
    ```

2. **Is the WebSocket URL right?** The endpoint is `${ROUTE_PREFIX}/ws`
   (default `/collab/ws`) and must be **`wss://`** in the browser when the page
   is HTTPS. `ws://` from an HTTPS page is blocked by the browser as mixed
   content.

3. **What does the client report?** The plugin surfaces structured errors via
   the `collab.onError(code, message, context)` hook and status via
   `onStatus`. Read the `code`; the sections below are organized by it.

## The WebSocket never opens (or opens then immediately closes)

Usually the reverse proxy isn't forwarding the WebSocket upgrade.

- The proxy must pass the `Upgrade` / `Connection` headers and **not buffer**
  the connection. See the ready nginx config in
  [Deployment → nginx](deployment.md#behind-nginx-websocket-proxying).
- The upgrade path must match `${ROUTE_PREFIX}/ws` exactly. A proxy that only
  routes `/collab/` (with a trailing slash) but not `/collab/ws` will drop it.
- Corporate proxies / firewalls sometimes block `wss`. Test from a network you
  control.

> **Tip: Quick raw test**
>
> ```bash
> # Should print HTTP/1.1 101 Switching Protocols on success:
> curl -i -N -H 'Connection: Upgrade' -H 'Upgrade: websocket' \
>   -H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZQ==' \
>   https://collab.example.com/collab/ws
> ```

## The connection closes after 30 to 60 seconds of inactivity

An idle proxy/load-balancer timeout is closing the socket. The server sends a
WebSocket **ping every 30 seconds** and the client sends an application `ping`
too, but a proxy with a shorter idle timeout will still cut it.

- Raise the proxy's WebSocket idle timeout (e.g. nginx
  `proxy_read_timeout`/`proxy_send_timeout` to `3600s`).
- The reference client reconnects automatically and resumes with a `sinceSeq`
  delta (no lost acknowledged edits), so brief drops self-heal. Constant
  30-second cycling means the proxy timeout is too low.

## `auth_required` / `auth_failed`: authentication

`context: "auth"`, the connection is **closed**.

| Symptom             | Cause                                                                                                                               | Fix                                                                                                                                 |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `auth_required`     | No `checkAuthentication` hook **and** `ALLOW_ANONYMOUS` is off (the default)                                                        | Configure a `checkAuthentication` hook, or set `ALLOW_ANONYMOUS=true` for evaluation only.                                          |
| `auth_failed`       | A token was presented but your hook returned `null` (invalid/expired), or an invalid token in anonymous mode with a hook configured | Return a valid identity from the hook; use a **token-provider function** (`collab.token: () => …`) so reconnects fetch a fresh JWT. |
| `forbidden` on join | Your `authorize` hook denied the `read` action for this user/document                                                               | Grant `read`, or route the user to a document they may access.                                                                      |

See [Authentication](authentication.md) for the resolution flow and the JWT
example.

## `protocol_mismatch`: version skew

`context: "protocol"`, connection closed. The `@jodit/collab-plugin` plugin and the
server negotiate a wire `PROTOCOL_VERSION` in `hello`/`welcome`; a mismatch
means the client and server are on **incompatible** versions. Upgrade both to
compatible releases (the protocol version only changes on a breaking wire
change). See [Licensing → Support & versioning](licensing.md#support-versioning).

## `base_seq_too_old`: a lagging client

`context: "submit"`, connection stays open. The client fell further behind than
the server's in-memory OT window (`SESSION_WINDOW`, default 512 entries), e.g.
after a long offline period. The reference client **auto-resyncs** from a fresh
snapshot; you should see it recover within a moment. If it happens constantly
under heavy load, raise `SESSION_WINDOW` (at the cost of more memory per room).

## `rate_limited`: throttling

`context: "submit"`, connection stays open. Either the per-connection submit
bucket is exhausted (a flood, or a script) or the server hit `MAX_ROOMS` when
opening a new document. Honest typing never trips the submit limit; tune
`RATE_LIMIT_*` / `MAX_ROOMS` in [Configuration](configuration.md) if a
legitimate workload needs more headroom.

## The REST endpoints (history / html / delete)

- `401` / `403`: same auth rules as the WebSocket. Pass the token as
  `Authorization: Bearer <token>` (preferred) or `?token=` (note the query
  string can leak into access logs, see [Security](security.md)).
- `404`: unknown document id.
- Behind a proxy these are ordinary HTTP; if they work but the WebSocket does
  not, the problem is specifically the WS upgrade (see above).

## Documents diverge / edits "fight" across nodes

The sequencer is **single-node per document**. Running two server instances
against the same documents at the same time is unsupported and will corrupt
history; the `(doc_id, seq)` primary key makes an accidental second writer fail
loudly rather than silently fork. Shard documents across nodes if you scale
horizontally, and never overlap writers during blue/green deploys. See
[Deployment → scaling caveats](deployment.md#scaling-caveats).

## Still stuck?

Collect: the client `onError`/`onStatus` output, the server logs around the
failed connection, your proxy config, and the output of the raw upgrade `curl`
above, then contact support via <https://xdsoft.net/jodit/pro/>.
