immich-app/immich · error · BadRequestException
Invalid license key
Error message
Invalid license key
What it means
setLicense performs a quick prefix check on the license key before cryptographic verification: a valid key must start with 'IMCL-' (client) or 'IMSV-' (server). Keys with any other prefix are rejected immediately with a 400 Bad Request and never reach signature validation.
Solutions
- Verify the license key starts exactly with 'IMCL-' or 'IMSV-' (no leading whitespace or missing characters).
- Re-copy the full license key from the purchase email/account portal.
- Ensure you are not pasting the activation key into the license key field (the two are separate inputs).
- Confirm the key belongs to this Immich product edition; keys from unrelated products will have a different prefix.
Example fix
// before
await api.setLicense({ licenseKey: 'XXXX-1234', activationKey: actKey });
// after
const key = licenseInput.trim();
if (!key.startsWith('IMCL-') && !key.startsWith('IMSV-')) {
throw new Error('license key must start with IMCL- or IMSV-');
}
await api.setLicense({ licenseKey: key, activationKey: actKey }); Defensive patterns
Strategy: validation
Validate before calling
const hasValidLicensePrefix = (key: string) => key.startsWith('IMCL-') || key.startsWith('IMSV-');
if (!hasValidLicensePrefix(licenseKey.trim())) {
throw new Error('license key must start with IMCL- or IMSV-');
} Type guard
const isLicenseKey = (v: unknown): v is `IMCL-${string}` | `IMSV-${string}` =>
typeof v === 'string' && (v.startsWith('IMCL-') || v.startsWith('IMSV-')); Try / catch
try {
await api.setLicense({ licenseKey, activationKey });
} catch (e) {
if (e instanceof BadRequestException && e.message === 'Invalid license key') {
// show format/validity guidance to user
}
} Prevention
- Trim whitespace/newlines from pasted keys.
- Validate the IMCL-/IMSV- prefix client-side before submitting.
- Keep license key and activation key fields distinct; never swap them.
- Copy the full key from the source in one action to avoid truncation.
When it happens
Trigger: POST /users/license with a licenseKey string that does not start with 'IMCL-' or 'IMSV-' — typos, truncated keys, license keys from a different product, or pasting only the activation key into the license key field.
Common situations: Copy/paste losing part of the key (missing prefix); confusing the license key with the activation key; purchasing/licensing flow mixing up Immich product editions; whitespace or BOM corrupting the prefix.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Invalid license key
- assetIds, albumId, or userId is required
- At least two people are required for merging
- Cannot request to join your own cluster group
- error instanceof Error ? error.message : error
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/b62498a80c340baa.
Report an issue: GitHub.
Appendix: 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 e55ac299a4)