{"record":{"id":"ee99cfca97d7dc80","repo":"toeverything/AFFiNE","slug":"unsupported-client-version-ee99cf","errorCode":"unsupported_client_version","errorMessage":"Unsupported client with version [${clientVersion}], required version is [${requiredVersion}].","messagePattern":"Unsupported client with version \\[(.+?)\\], required version is \\[(.+?)\\]\\.","errorType":"exception","errorClass":"UnsupportedClientVersion","httpStatus":403,"severity":"error","filePath":"packages/backend/server/src/core/realtime/gateway.ts","lineNumber":165,"sourceCode":"  @OnEvent('realtime.topic.changed', { suppressError: true })\n  onRealtimeTopicChanged(payload: RealtimePublishPayload) {\n    try {\n      this.publisher.publishLocal(payload);\n    } catch (error) {\n      this.logger.error('Failed to publish realtime event', error);\n    }\n  }\n\n  private assertVersion(clientVersion?: string) {\n    const normalized = clientVersion\n      ? normalizeRealtimeClientVersion(clientVersion)\n      : null;\n    if (\n      !normalized ||\n      !semver.valid(normalized) ||\n      !MIN_REALTIME_CLIENT_VERSION.test(normalized)\n    ) {\n      throw new UnsupportedClientVersion({\n        clientVersion: clientVersion ?? 'unset_or_invalid',\n        requiredVersion: '>=0.26.0',\n      });\n    }\n  }\n}\n","sourceCodeStart":147,"sourceCodeEnd":172,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/core/realtime/gateway.ts#L147-L172","documentation":"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.","triggerScenarios":"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').","commonSituations":"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.","solutions":["Upgrade the client to >=0.26.0 and send its real semver in the connection's version param.","Custom clients: pass a valid semver string (e.g. from package.json) with every connect.","After upgrading the server, re-check MIN_REALTIME_CLIENT_VERSION and raise the client floor accordingly."],"exampleFix":"// before\nconst ws = new WebSocket(`${rtUrl}/global/sync`); // no version param\n\n// after\nconst ws = new WebSocket(`${rtUrl}/global/sync?version=${pkg.version}`); // pkg.version >= 0.26.0","handlingStrategy":"validation","validationCode":"import semver from 'semver';\nconst MIN_REALTIME_CLIENT_VERSION = '0.26.0';\nfunction clientSupported(v?: string) {\n  return !!v && semver.valid(v) !== null && semver.gte(v, MIN_REALTIME_CLIENT_VERSION);\n}\nif (!clientSupported(clientVersion)) {\n  throw new Error('client version unsupported, update required');\n}","typeGuard":"const isUnsupportedClientVersion = (e: unknown): e is UnsupportedClientVersion =>\n  e instanceof UnsupportedClientVersion;","tryCatchPattern":"client.on('connect_error', (e) => {\n  if ((e as { code?: string }).code === 'unsupported_client_version') {\n    return promptForUpdate();\n  }\n  throw e;\n});","preventionTips":["Always ship the app's package version as the realtime version param.","Keep an e2e test that connects with exactly the minimum supported client version."],"tags":["realtime","versioning","semver","websocket"],"backgroundTag":"client-version-unsupported","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}