toeverything/AFFiNE · error · UnsupportedClientVersion

unsupported_client_version

unsupported_client_version

Error message

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

What it means

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.

Solutions

  1. Update the client to a version satisfying the required range shown in the message (stable >= 0.25.0)
  2. Ensure native/embedded clients always send a valid semver in the client version header
  3. For local dev builds, stamp a real semver (e.g. 0.25.0-dev.1) instead of a placeholder
  4. Server operators: review/adjust the required client version configuration when intentionally raising the floor

Example fix

// before
const headers = { 'x-affine-client-kind': 'native' };

// after
const headers = {
  'x-affine-client-kind': 'native',
  'x-affine-client-version': app.getVersion(), // must satisfy required semver range, e.g. >=0.25.0
};
Defensive patterns

Strategy: fallback

Validate before calling

import semver from 'semver';
const HARD_REQUIRED_VERSION = '>=0.25.0';
function isClientVersionAcceptable(version: string | undefined): boolean {
  return !!version && semver.valid(version) !== null && semver.satisfies(version, HARD_REQUIRED_VERSION);
}

Try / catch

try {
  await api.get(url);
} catch (e) {
  if (isAffineErrorCode(e, 'unsupported_client_version')) {
    showUpdateRequired(); // hard fallback: no retry can fix an old binary
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at packages/backend/server/src/core/auth/guard.ts:216

      .authSessionId;
    if (authSessionId) {
      await this.authSessions.revoke(
        authSessionId,
        'unsupported_client_version',
        session.user.id
      );
    } else {
      await this.auth.signOut(session.sessionId);
    }
    if (res && !authSessionId) {
      await this.auth.refreshCookies(res, session.sessionId);
    }

    if (isPublic && !authSessionId) {
      return false;
    }

    throw new UnsupportedClientVersion({
      clientVersion: clientVersion ?? 'unset_or_invalid',
      requiredVersion: versionCheckResult.requiredVersion,
    });
  }

  private getVersionRange(versionRange: string): semver.Range | null {
    if (this.cachedVersionRange.has(versionRange)) {
      // oxlint-disable-next-line typescript/no-non-null-assertion
      return this.cachedVersionRange.get(versionRange)!;
    }

    let range: semver.Range | null = null;
    try {
      range = new semver.Range(versionRange, { loose: false });
      if (!semver.validRange(range)) {
        range = null;
      }
    } catch {

View on GitHub (pinned to 591f874dad)