toeverything/AFFiNE · error · LicenseExpired
license_expired
license_expired
Error message
License has expired.
What it means
installLicense() persists a self-hosted team license file after cryptographically resolving it with resolveWorkspaceTeamLicense. It throws LicenseExpired when the resolved entitlement is not valid — in practice the resolution step has already flagged the license as past its end date (errorCode 'expired_end_at'). The install is aborted before any installedLicense row is written.
Source
Thrown at packages/backend/server/src/plugins/license/service.ts:112
select: {
installedAt: true,
validatedAt: true,
expiredAt: true,
quantity: true,
recurring: true,
variant: true,
},
where: {
workspaceId,
},
});
}
@Transactional()
async installLicense(workspaceId: string, license: Buffer) {
const resolved = this.resolveWorkspaceTeamLicense(workspaceId, license);
if (!resolved.valid) {
throw new LicenseExpired();
}
const validatedAt = new Date();
const previous = await this.db.installedLicense.findUnique({
where: { workspaceId },
});
const installed = await this.db.installedLicense.upsert({
where: { workspaceId },
update: {
key: this.licenseSubjectId(resolved),
quantity: this.licenseQuantity(resolved),
recurring: this.licenseRecurring(resolved),
variant: SubscriptionVariant.Onetime,
validateKey: '',
validatedAt,
expiredAt: this.licenseExpiresAt(resolved),
license,View on GitHub (pinned to b4c8548c09)
Solutions
- Generate/download a fresh license from https://app.affine.pro (or Affine support) for the current period and upload that file.
- Verify the server's date/time and NTP sync — an ahead-of-time clock invalidates otherwise valid licenses.
- If the license should still be valid, contact Affine support with the license subject id, since endAt is inside the signed payload and cannot be edited.
Defensive patterns
Strategy: try-catch
Type guard
function isLicenseExpired(e: unknown): e is LicenseExpired {
return e instanceof LicenseExpired; // code === 'license_expired'
} Try / catch
try {
await licenseService.installLicense(workspaceId, licenseBuffer);
} catch (e) {
if (e instanceof LicenseExpired) {
// show 'license expired, fetch a new file' and link to app.affine.pro
} else throw e;
} Prevention
- Track license endAt (visible via preview) and alert admins before renewal date.
- Keep server clocks NTP-synced so valid licenses do not read as expired.
- Store renewed license files in a known location so the old file is not re-uploaded.
When it happens
Trigger: Uploading a license file whose signed end date is in the past via the self-hosted admin 'install license' flow (installLicense(workspaceId, licenseBuffer)); re-installing an old license after it lapsed; server clock set past the license window.
Common situations: Renewal gap: the previous yearly license expired and the admin uploads the old file instead of the new one; VM clock drift after snapshot restore makes a valid license appear expired; staging environments reusing expired test licenses.
Related errors
- bad_request
- invalid_license_to_activate
- workspace_license_already_exists
- license_not_found
- invalid_license_to_activate
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/f3b6b1794ae9ce80.
Report an issue: GitHub.