{"record":{"id":"b62498a80c340baa","repo":"immich-app/immich","slug":"invalid-license-key-b62498","errorCode":null,"errorMessage":"Invalid license key","messagePattern":"Invalid license key","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/user.service.ts","lineNumber":177,"sourceCode":"  async getLicense(auth: AuthDto): Promise<LicenseResponseDto> {\n    const metadata = await this.userRepository.getMetadata(auth.user.id);\n\n    const license = metadata.find(\n      (item): item is UserMetadataItem<UserMetadataKey.License> => item.key === UserMetadataKey.License,\n    );\n    if (!license) {\n      throw new NotFoundException();\n    }\n    return { ...license.value, activatedAt: new Date(license.value.activatedAt) };\n  }\n\n  async deleteLicense({ user }: AuthDto): Promise<void> {\n    await this.userRepository.deleteMetadata(user.id, UserMetadataKey.License);\n  }\n\n  async setLicense(auth: AuthDto, license: LicenseKeyDto): Promise<LicenseResponseDto> {\n    if (!license.licenseKey.startsWith('IMCL-') && !license.licenseKey.startsWith('IMSV-')) {\n      throw new BadRequestException('Invalid license key');\n    }\n\n    const { licensePublicKey } = this.configRepository.getEnv();\n\n    const isClientLicenseValid = this.cryptoRepository.verifySha256(\n      license.licenseKey,\n      license.activationKey,\n      licensePublicKey.client,\n    );\n\n    const isServerLicenseValid = this.cryptoRepository.verifySha256(\n      license.licenseKey,\n      license.activationKey,\n      licensePublicKey.server,\n    );\n\n    if (!isClientLicenseValid && !isServerLicenseValid) {\n      throw new BadRequestException('Invalid license key');","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/user.service.ts#L159-L195","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Trim whitespace and verify the key starts with 'IMCL-' or 'IMSV-' before submitting.","Re-copy the license key from the original purchase/source, ensuring the full string including prefix.","Separate the license key and activation key into distinct form fields to avoid cross-pasting.","If the key legitimately lacks the prefix, obtain a correctly formatted key from the issuer."],"exampleFix":"// before\nawait api.setLicense({ licenseKey: 'ABCD-1234', activationKey: '...' }); // 400\n\n// after\nconst key = rawKey.trim();\nif (!key.startsWith('IMCL-') && !key.startsWith('IMSV-')) {\n  showFormatError();\n  return;\n}\nawait api.setLicense({ licenseKey: key, activationKey });","handlingStrategy":"validation","validationCode":"function isValidLicenseKeyFormat(key) {\n  const k = (key ?? '').trim();\n  return k.startsWith('IMCL-') || k.startsWith('IMSV-');\n}\nif (!isValidLicenseKeyFormat(licenseKey)) { showFormatError(); return; }","typeGuard":"const hasValidLicensePrefix = (k: string): boolean =>\n  k.trim().startsWith('IMCL-') || k.trim().startsWith('IMSV-');","tryCatchPattern":"try {\n  await api.setLicense({ licenseKey, activationKey });\n} catch (e) {\n  if ((e as any).status === 400 && (e as any).message === 'Invalid license key') {\n    setFieldError('licenseKey', 'License key must start with IMCL- or IMSV-');\n    return;\n  }\n  throw e;\n}","preventionTips":["Trim whitespace and check the prefix before submit.","Keep license key and activation key in separate fields to avoid cross-paste.","Re-copy the full key from the source if the prefix is missing."],"tags":["license","validation","format-check","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}