{"record":{"id":"d67e33703ac246e2","repo":"immich-app/immich","slug":"failed-to-fetch-activation-key","errorCode":null,"errorMessage":"Failed to fetch activation key","messagePattern":"Failed to fetch activation key","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/lib/utils/license-utils.ts","lineNumber":20,"sourceCode":"import { PUBLIC_IMMICH_BUY_HOST, PUBLIC_IMMICH_PAY_HOST } from '$env/static/public';\nimport type { ImmichProduct } from '$lib/constants';\nimport { authManager } from '$lib/managers/auth-manager.svelte';\nimport { serverConfigManager } from '$lib/managers/server-config-manager.svelte';\n\nexport const activateProduct = async (licenseKey: string, activationKey: string): Promise<LicenseResponseDto> => {\n  // TODO is this needed?\n  await authManager.load();\n\n  const isServerActivation = authManager.user.isAdmin && licenseKey.search('IMSV') !== -1;\n  const licenseKeyDto = { licenseKey, activationKey };\n  // Send server key to user activation if user is not admin\n  return isServerActivation ? setServerLicense({ licenseKeyDto }) : setUserLicense({ licenseKeyDto });\n};\n\nexport const getActivationKey = async (licenseKey: string): Promise<string> => {\n  const response = await fetch(new URL(`/api/v1/activate/${licenseKey}`, PUBLIC_IMMICH_PAY_HOST).href);\n  if (!response.ok) {\n    throw new Error('Failed to fetch activation key');\n  }\n  return response.text();\n};\n\nexport const getLicenseLink = (license: ImmichProduct) => {\n  const url = new URL('/', PUBLIC_IMMICH_BUY_HOST);\n  url.searchParams.append('productId', license);\n  url.searchParams.append('instanceUrl', serverConfigManager.value.externalDomain || globalThis.origin);\n  return url.href;\n};\n","sourceCodeStart":2,"sourceCodeEnd":31,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/web/src/lib/utils/license-utils.ts#L2-L31","documentation":"Thrown by getActivationKey (web/src/lib/utils/license-utils.ts) when the fetch to PUBLIC_IMMICH_PAY_HOST/api/v1/activate/<licenseKey> returns a non-ok HTTP status. This is the client-side call that exchanges a license key for an activation key before sending it to the Immich server.","triggerScenarios":"User enters a license key in the purchase/activation UI; the external payment/activation endpoint returns 4xx/5xx (invalid key, already-activated key, server down, network/CORS error).","commonSituations":"Typo in the license key; key already activated on another instance; the IMMICH_PAY service is temporarily unavailable; ad-blocker or network policy blocks the external host; CORS misconfiguration; expired license.","solutions":["Double-check the license key for typos and that it has not been activated elsewhere.","Retry later if the IMMICH_PAY service is having an outage (check status.immich.app / community).","Disable ad-blockers/privacy extensions that may block the external request, or whitelist PUBLIC_IMMICH_PAY_HOST.","Contact Immich support if the key is valid but activation keeps failing."],"exampleFix":"// before\nconst key = await getActivationKey(licenseKey); // throws on non-ok\n\n// after — surface the status for diagnosis\nconst res = await fetch(new URL(`/api/v1/activate/${licenseKey}`, PUBLIC_IMMICH_PAY_HOST));\nif (!res.ok) throw new Error(`Activation failed: ${res.status} ${res.statusText}`);\nconst key = await res.text();","handlingStrategy":"retry","validationCode":"// validate key format before hitting the endpoint\nconst re = /^[A-Z0-9-]{6,}$/i;\nif (!re.test(licenseKey.trim())) { notify('Invalid key format'); return; }","typeGuard":"const looksLikeLicenseKey = (k: string): boolean => typeof k === 'string' && k.trim().length >= 6;","tryCatchPattern":"try { return await getActivationKey(licenseKey); }\ncatch (e) { if (/Failed to fetch activation key/.test(e.message)) { await sleep(backoff); return getActivationKey(licenseKey); } throw e; }","preventionTips":["Validate key format client-side before calling the external endpoint.","Whitelist PUBLIC_IMMICH_PAY_HOST in ad-blockers/privacy extensions.","Retry transient failures with backoff before surfacing an error."],"tags":["license","activation","network","external-api","web"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}