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

  1. Generate/download a fresh license from https://app.affine.pro (or Affine support) for the current period and upload that file.
  2. Verify the server's date/time and NTP sync — an ahead-of-time clock invalidates otherwise valid licenses.
  3. 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

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


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