immich-app/immich · error · BadRequestException

Invalid license key

Error message

Invalid license key

What it means

A BadRequestException (HTTP 400) thrown by UserService.setLicense before any cryptographic verification, when the supplied licenseKey does not begin with either 'IMCL-' (client license) or 'IMSV-' (server license). This is a fast-fail format check on the key prefix.

Source

Thrown at server/src/services/user.service.ts:177

  async getLicense(auth: AuthDto): Promise<LicenseResponseDto> {
    const metadata = await this.userRepository.getMetadata(auth.user.id);

    const license = metadata.find(
      (item): item is UserMetadataItem<UserMetadataKey.License> => item.key === UserMetadataKey.License,
    );
    if (!license) {
      throw new NotFoundException();
    }
    return { ...license.value, activatedAt: new Date(license.value.activatedAt) };
  }

  async deleteLicense({ user }: AuthDto): Promise<void> {
    await this.userRepository.deleteMetadata(user.id, UserMetadataKey.License);
  }

  async setLicense(auth: AuthDto, license: LicenseKeyDto): Promise<LicenseResponseDto> {
    if (!license.licenseKey.startsWith('IMCL-') && !license.licenseKey.startsWith('IMSV-')) {
      throw new BadRequestException('Invalid license key');
    }

    const { licensePublicKey } = this.configRepository.getEnv();

    const isClientLicenseValid = this.cryptoRepository.verifySha256(
      license.licenseKey,
      license.activationKey,
      licensePublicKey.client,
    );

    const isServerLicenseValid = this.cryptoRepository.verifySha256(
      license.licenseKey,
      license.activationKey,
      licensePublicKey.server,
    );

    if (!isClientLicenseValid && !isServerLicenseValid) {
      throw new BadRequestException('Invalid license key');

View on GitHub (pinned to 199723261c)

Solutions

  1. Trim whitespace and verify the key starts with 'IMCL-' or 'IMSV-' before submitting.
  2. Re-copy the license key from the original purchase/source, ensuring the full string including prefix.
  3. Separate the license key and activation key into distinct form fields to avoid cross-pasting.
  4. If the key legitimately lacks the prefix, obtain a correctly formatted key from the issuer.

Example fix

// before
await api.setLicense({ licenseKey: 'ABCD-1234', activationKey: '...' }); // 400

// after
const key = rawKey.trim();
if (!key.startsWith('IMCL-') && !key.startsWith('IMSV-')) {
  showFormatError();
  return;
}
await api.setLicense({ licenseKey: key, activationKey });
Defensive patterns

Strategy: validation

Validate before calling

function isValidLicenseKeyFormat(key) {
  const k = (key ?? '').trim();
  return k.startsWith('IMCL-') || k.startsWith('IMSV-');
}
if (!isValidLicenseKeyFormat(licenseKey)) { showFormatError(); return; }

Type guard

const hasValidLicensePrefix = (k: string): boolean =>
  k.trim().startsWith('IMCL-') || k.trim().startsWith('IMSV-');

Try / catch

try {
  await api.setLicense({ licenseKey, activationKey });
} catch (e) {
  if ((e as any).status === 400 && (e as any).message === 'Invalid license key') {
    setFieldError('licenseKey', 'License key must start with IMCL- or IMSV-');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /users/license with a licenseKey that is malformed, missing the prefix, copied with whitespace, or pasted from a source that stripped the prefix. The check happens before signature verification.

Common situations: Typo in the key; user pastes only the activation key field into the license key field; copy-paste includes leading/trailing spaces; key from a different licensing system.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/b62498a80c340baa. Report an issue: GitHub.