toeverything/AFFiNE · error · UnsupportedClientVersion

unsupported_client_version

unsupported_client_version

Error message

Unsupported client with version [${clientVersion}], required version is [${requiredVersion}].

What it means

Thrown by RealtimeGateway.assertVersion (packages/backend/server/src/core/realtime/gateway.ts:165) at connection time. The client-supplied version must survive normalizeRealtimeClientVersion, pass semver.valid, and satisfy MIN_REALTIME_CLIENT_VERSION (currently >=0.26.0); otherwise UnsupportedClientVersion rejects the connection before any realtime traffic flows.

Solutions

  1. Upgrade the client to >=0.26.0 and send its real semver in the connection's version param.
  2. Custom clients: pass a valid semver string (e.g. from package.json) with every connect.
  3. After upgrading the server, re-check MIN_REALTIME_CLIENT_VERSION and raise the client floor accordingly.

Example fix

// before
const ws = new WebSocket(`${rtUrl}/global/sync`); // no version param

// after
const ws = new WebSocket(`${rtUrl}/global/sync?version=${pkg.version}`); // pkg.version >= 0.26.0
Defensive patterns

Strategy: validation

Validate before calling

import semver from 'semver';
const MIN_REALTIME_CLIENT_VERSION = '0.26.0';
function clientSupported(v?: string) {
  return !!v && semver.valid(v) !== null && semver.gte(v, MIN_REALTIME_CLIENT_VERSION);
}
if (!clientSupported(clientVersion)) {
  throw new Error('client version unsupported, update required');
}

Type guard

const isUnsupportedClientVersion = (e: unknown): e is UnsupportedClientVersion =>
  e instanceof UnsupportedClientVersion;

Try / catch

client.on('connect_error', (e) => {
  if ((e as { code?: string }).code === 'unsupported_client_version') {
    return promptForUpdate();
  }
  throw e;
});

Prevention

When it happens

Trigger: Opening the realtime WebSocket/SSE endpoint with a version param below 0.26.0, a non-semver string, or no version at all (reported as 'unset_or_invalid').

Common situations: Old desktop/web client after a server upgrade, a custom bot that never sends a version, or a dev client with a placeholder version string after the minimum was bumped.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/ee99cfca97d7dc80. Report an issue: GitHub.

Appendix: source

Thrown at packages/backend/server/src/core/realtime/gateway.ts:165

  @OnEvent('realtime.topic.changed', { suppressError: true })
  onRealtimeTopicChanged(payload: RealtimePublishPayload) {
    try {
      this.publisher.publishLocal(payload);
    } catch (error) {
      this.logger.error('Failed to publish realtime event', error);
    }
  }

  private assertVersion(clientVersion?: string) {
    const normalized = clientVersion
      ? normalizeRealtimeClientVersion(clientVersion)
      : null;
    if (
      !normalized ||
      !semver.valid(normalized) ||
      !MIN_REALTIME_CLIENT_VERSION.test(normalized)
    ) {
      throw new UnsupportedClientVersion({
        clientVersion: clientVersion ?? 'unset_or_invalid',
        requiredVersion: '>=0.26.0',
      });
    }
  }
}

View on GitHub (pinned to b4c8548c09)