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
- 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
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
- 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
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)