{"record":{"id":"9070d858cf2b5a4e","repo":"toeverything/AFFiNE","slug":"unsupported-client-version-9070d8","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/version/service.ts","lineNumber":28,"sourceCode":"@Injectable()\nexport class VersionService {\n  private readonly logger = new Logger(VersionService.name);\n  private static readonly HARD_REQUIRED_VERSION = '>=0.25.0';\n  private static readonly CANARY_REQUIRED_VERSION = 'canary (within 2 months)';\n\n  constructor(private readonly config: Config) {}\n\n  async checkVersion(clientVersion?: string) {\n    const requiredVersion = this.config.client.versionControl.requiredVersion;\n\n    if (clientVersion && env.namespaces.canary) {\n      const canaryCheck = checkCanaryDateClientVersion(clientVersion);\n      if (canaryCheck.matched) {\n        if (canaryCheck.allowed) {\n          return true;\n        }\n\n        throw new UnsupportedClientVersion({\n          clientVersion,\n          requiredVersion: VersionService.CANARY_REQUIRED_VERSION,\n        });\n      }\n    }\n\n    const hardRange = await this.getVersionRange(\n      VersionService.HARD_REQUIRED_VERSION\n    );\n    const configRange = await this.getVersionRange(requiredVersion);\n\n    if (\n      configRange &&\n      (!clientVersion ||\n        !semver.satisfies(clientVersion, configRange, {\n          includePrerelease: true,\n        }))\n    ) {","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/version/service.ts#L10-L46","documentation":"Thrown by VersionService.checkVersion() when the client version is not allowed. There are three trigger paths: (a) on the canary namespace, a canary-style client version older than ~2 months is rejected; (b) the client version does not satisfy the configured requiredVersion range; (c) the client version does not satisfy the hard floor '>=0.25.0'. A missing/invalid clientVersion is treated as a failure against any active range.","triggerScenarios":"Client connects with an x-affine-version (or canary version) that is too old relative to the server's HARD_REQUIRED_VERSION, the config requiredVersion, or (for canary builds) older than the 2-month canary window. Also fires when the version header is absent and a range is active.","commonSituations":"User running an outdated client against a newer server; server upgraded its requiredVersion config without a matching client release; canary build not updated within 2 months; client not sending the version header at all.","solutions":["Update the client to a version that satisfies the server's requiredVersion range.","If you control the server, align config.client.versionControl.requiredVersion with the deployed client range.","Ensure the client sends the correct version header on every request/socket handshake.","For canary builds, refresh the build within the 2-month window."],"exampleFix":"// before — server requires >=0.25.0 but client is 0.20.1\nheaders: { 'x-affine-version': '0.20.1' }\n\n// after\nheaders: { 'x-affine-version': '0.26.0' }","handlingStrategy":"validation","validationCode":"import semver from 'semver';\nconst clientVersion = getAppVersion();\nif (!semver.satisfies(clientVersion, REQUIRED_RANGE)) { promptUpgrade(); return; }","typeGuard":"function isClientVersionAllowed(version: string | undefined, range: string): boolean {\n  return Boolean(version) && semver.satisfies(version, range, { includePrerelease: true });\n}","tryCatchPattern":"try {\n  await connect();\n} catch (e) {\n  if (e?.code === 'unsupported_client_version') { forceUpgrade(); return; }\n  throw e;\n}","preventionTips":["Always send the correct version header on requests and socket handshakes.","Prompt in-app upgrades when the client detects it is below the server's required range.","Keep config.client.versionControl.requiredVersion aligned with the deployed client range.","Refresh canary builds within the 2-month window."],"tags":["versioning","compatibility","config","semver"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}