immich-app/immich · error · Error
Failed to fetch activation key
Error message
Failed to fetch activation key
What it means
getActivationKey() fetches a license activation key from PUBLIC_IMMICH_PAY_HOST (/api/v1activate/<licenseKey>) and throws if the HTTP response is not ok. This means the license-purchase service rejected the request — bad license key, network failure, or service error — so the license cannot be activated client-side.
Solutions
- Verify the license key is copied exactly (no extra whitespace) and is unused/not expired.
- Check network reachability of the payment host (disable ad-blockers/VPN blocking it) and retry.
- Confirm your immich version's activation flow matches the hosted service; update the app if outdated.
- If the service is down, retry later or contact Immich support with your purchase details.
- Inspect the HTTP status of the fetch response to distinguish 404 (bad key) from 5xx (service issue).
Example fix
// before
const key = await getActivationKey(' IM-XXXX '); // whitespace-corrupted key
// after
const key = await getActivationKey(licenseKey.trim()); Defensive patterns
Strategy: try-catch
Validate before calling
if (!licenseKey || licenseKey.trim().length === 0) throw new ValidationError('License key is required');
if (typeof PUBLIC_IMMICH_PAY_HOST !== 'string' || !PUBLIC_IMMICH_PAY_HOST.startsWith('https://')) throw new ConfigError('Bad activation host'); Try / catch
try { key = await getActivationKey(licenseKey.trim()); } catch (e) { if (e.message === 'Failed to fetch activation key') showToast(t('license.activation_failed')); else throw e; } Prevention
- Trim and validate license keys before activation
- Check ad-blocker/VPN interference with the payment host
- Handle non-ok responses with status-specific messaging for support
- Retry activation on 5xx since the remote service may be temporarily down
When it happens
Trigger: Calling getActivationKey(licenseKey) (from the license activation flow) when the remote activation endpoint returns 4xx/5xx or the fetch otherwise yields !response.ok.
Common situations: Entering a mistyped or already-used license key; PUBLIC_IMMICH_PAY_HOST unreachable (firewall/ad-blocker/ VPN); immich pay service downtime; server key generated by an incompatible version.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- await response.text()
- EOFException
- errors.unable_to_upload_file
- Machine learning request to
- Server is not reachable
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/d67e33703ac246e2.
Report an issue: GitHub.
Appendix: source
Thrown at web/src/lib/utils/license-utils.ts:20
import { PUBLIC_IMMICH_BUY_HOST, PUBLIC_IMMICH_PAY_HOST } from '$env/static/public';
import type { ImmichProduct } from '$lib/constants';
import { authManager } from '$lib/managers/auth-manager.svelte';
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
export const activateProduct = async (licenseKey: string, activationKey: string): Promise<LicenseResponseDto> => {
// TODO is this needed?
await authManager.load();
const isServerActivation = authManager.user.isAdmin && licenseKey.search('IMSV') !== -1;
const licenseKeyDto = { licenseKey, activationKey };
// Send server key to user activation if user is not admin
return isServerActivation ? setServerLicense({ licenseKeyDto }) : setUserLicense({ licenseKeyDto });
};
export const getActivationKey = async (licenseKey: string): Promise<string> => {
const response = await fetch(new URL(`/api/v1/activate/${licenseKey}`, PUBLIC_IMMICH_PAY_HOST).href);
if (!response.ok) {
throw new Error('Failed to fetch activation key');
}
return response.text();
};
export const getLicenseLink = (license: ImmichProduct) => {
const url = new URL('/', PUBLIC_IMMICH_BUY_HOST);
url.searchParams.append('productId', license);
url.searchParams.append('instanceUrl', serverConfigManager.value.externalDomain || globalThis.origin);
return url.href;
};
View on GitHub (pinned to e55ac299a4)