{"record":{"id":"d2a6a049e637ca0a","repo":"toeverything/AFFiNE","slug":"unsupported-client-version","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/auth/guard.ts","lineNumber":216,"sourceCode":"      .authSessionId;\n    if (authSessionId) {\n      await this.authSessions.revoke(\n        authSessionId,\n        'unsupported_client_version',\n        session.user.id\n      );\n    } else {\n      await this.auth.signOut(session.sessionId);\n    }\n    if (res && !authSessionId) {\n      await this.auth.refreshCookies(res, session.sessionId);\n    }\n\n    if (isPublic && !authSessionId) {\n      return false;\n    }\n\n    throw new UnsupportedClientVersion({\n      clientVersion: clientVersion ?? 'unset_or_invalid',\n      requiredVersion: versionCheckResult.requiredVersion,\n    });\n  }\n\n  private getVersionRange(versionRange: string): semver.Range | null {\n    if (this.cachedVersionRange.has(versionRange)) {\n      // oxlint-disable-next-line typescript/no-non-null-assertion\n      return this.cachedVersionRange.get(versionRange)!;\n    }\n\n    let range: semver.Range | null = null;\n    try {\n      range = new semver.Range(versionRange, { loose: false });\n      if (!semver.validRange(range)) {\n        range = null;\n      }\n    } catch {","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/591f874dad30887a80143a061a44bd3ca7ee3299/packages/backend/server/src/core/auth/guard.ts#L198-L234","documentation":"AuthGuard semver-checks the client version (via getClientVersionFromRequest headers) against the server's required range: a hard floor of >=0.25.0 for stable builds, and canary builds must be no older than 2 months. Outside the range - or when the version header is missing/unparseable, reported as 'unset_or_invalid' - it throws UnsupportedClientVersion (unsupported_client_version) with both the sent and required versions in the message.","triggerScenarios":"A client older than 0.25.0 hitting any version-guarded endpoint; a canary build older than two months; no x-affine-client-version header on a guarded route; a non-semver version string like 'dev' or '0.0.0+local' failing semver parsing.","commonSituations":"Stale Electron or mobile app after a server upgrade; self-hosted users pinning old releases; custom embedders/scripts that never send a version header; CI builds stamped with non-semver versions; canary users returning after a hiatus.","solutions":["Update the client to a version satisfying the required range shown in the message (stable >= 0.25.0)","Ensure native/embedded clients always send a valid semver in the client version header","For local dev builds, stamp a real semver (e.g. 0.25.0-dev.1) instead of a placeholder","Server operators: review/adjust the required client version configuration when intentionally raising the floor"],"exampleFix":"// before\nconst headers = { 'x-affine-client-kind': 'native' };\n\n// after\nconst headers = {\n  'x-affine-client-kind': 'native',\n  'x-affine-client-version': app.getVersion(), // must satisfy required semver range, e.g. >=0.25.0\n};","handlingStrategy":"fallback","validationCode":"import semver from 'semver';\nconst HARD_REQUIRED_VERSION = '>=0.25.0';\nfunction isClientVersionAcceptable(version: string | undefined): boolean {\n  return !!version && semver.valid(version) !== null && semver.satisfies(version, HARD_REQUIRED_VERSION);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.get(url);\n} catch (e) {\n  if (isAffineErrorCode(e, 'unsupported_client_version')) {\n    showUpdateRequired(); // hard fallback: no retry can fix an old binary\n  } else throw e;\n}","preventionTips":["Stamp releases with valid semver and always send the client version header","Smoke-test upgrade paths against the oldest supported client","Watch release notes for required-version bumps before shipping server updates"],"tags":["auth","versioning","semver","client-compatibility"],"backgroundTag":"client-version-unsupported","analyzedSha":"591f874dad30887a80143a061a44bd3ca7ee3299","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}