toeverything/AFFiNE · error · InternalServerError

internal_server_error

internal_server_error

Error message

An internal error occurred.

What it means

remoteLicense() normalizes the response from the AFFiNE Pro license cloud API (activate/deactivate). After throwRemoteLicenseError confirms error is absent, an intact response must contain response.license; if it does not, the response is malformed and InternalServerError('Invalid AFFiNE Pro license response.') is thrown. This signals an unexpected protocol violation from the license service rather than a client mistake.

Source

Thrown at packages/backend/server/src/plugins/license/service.ts:463

        e instanceof UserFriendlyError &&
        e.name !== 'internal_server_error'
      ) {
        this.event.emit('workspace.subscription.canceled', {
          workspaceId: license.workspaceId,
          plan: SubscriptionPlan.SelfHostedTeam,
          recurring: SubscriptionRecurring.Monthly,
        });
        await this.entitlement.revokeBySubject('selfhost_license', license.key);
      }

      return null;
    }
  }

  private remoteLicense(response: LicenseResponse) {
    this.throwRemoteLicenseError(response.error);
    if (!response.license) {
      throw new InternalServerError('Invalid AFFiNE Pro license response.');
    }
    return response.license;
  }

  private remoteCommand(response: CommandResponse) {
    this.throwRemoteLicenseError(response.error);
  }

  private remotePortal(response: PortalResponse) {
    this.throwRemoteLicenseError(response.error);
    if (!response.url) {
      throw new InternalServerError('Invalid AFFiNE Pro portal response.');
    }
    return { url: response.url };
  }

  private throwRemoteLicenseError(
    error?: LicenseError | null

View on GitHub (pinned to b4c8548c09)

Solutions

  1. Retry once after a short delay — malformed 2xx bodies are usually transient.
  2. Inspect egress: disable any corporate proxy/SSL-inspection rewriting for https://app.affine.pro, or fix the reverse-proxy body-filter rules.
  3. Capture the raw response (log licenseClient traffic in dev) and check the AFFiNE Pro status page / support if the shape changed — a persistent failure indicates an upstream contract change requiring a server update.
Defensive patterns

Strategy: retry

Type guard

function isInternalServerError(e: unknown): e is InternalServerError {
  return e instanceof InternalServerError;
}

Try / catch

async function withLicenseRetry<T>(fn: () => Promise<T>, attempts = 2): Promise<T> {
  for (let i = 0; ; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i >= attempts || !(e instanceof InternalServerError)) throw e;
      await new Promise(r => setTimeout(r, 500 * 2 ** i));
    }
  }
}

Prevention

When it happens

Trigger: licenseClient.activate({ licenseKey }) or deactivate returning HTTP 2xx with an empty or schema-breaking body — e.g. a proxy (nginx/Cloudflare) intercepting and rewriting the response, an API version change on app.affine.pro, or a truncated response.

Common situations: Self-hosted deployments behind corporate proxies that strip or replace response bodies; transient CDN corruption; upstream deploying a breaking API change; responses buffered/served from cache incorrectly.

Related errors


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