immich-app/immich · error · UnauthorizedException

Invalid share key

Error message

Invalid share key

What it means

Shared-link (public sharing) authentication decodes the provided key as hex (when 100 chars) or base64url, looks it up in the shared_link table, and validates it exists and has not expired. If the repository returns nothing or the link is invalid/expired, an UnauthorizedException is thrown.

Solutions

  1. Regenerate the share link from the owner account and use the fresh key
  2. Verify the full key was copied (no truncation, correct URL-encoding)
  3. Have the owner extend or remove expiresAt on the share link
  4. Check the server database/backup to confirm the key still exists
Defensive patterns

Strategy: try-catch

Type guard

const isPlausibleKey = (k: string) => k.length === 100 || /^[A-Za-z0-9_-]+$/.test(k);

Try / catch

catch (e) { if (e.status === 401 && e.message === 'Invalid share key') { /* regenerate link */ } }

Prevention

When it happens

Trigger: GET with ?key=... where the key is malformed, deleted, revoked, or the share link expired (expiresAt in the past).

Common situations: Copying a truncated URL; share links revoked by the owner after password/key rotation; stale bookmarks after a migration that regenerated keys; expiry passing between generating and using the link.

Related errors


AI-assisted analysis of immich-app/immich@f48d4b3321 (2026-09-15). Data as JSON: /api/errors/c22811fb167fdb94. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/auth.service.ts:504

  }

  private getCookieOauthState(headers: IncomingHttpHeaders): string | null {
    const cookies = parse(headers.cookie || '');
    return cookies[ImmichCookie.OAuthState] || null;
  }

  private getCookieCodeVerifier(headers: IncomingHttpHeaders): string | null {
    const cookies = parse(headers.cookie || '');
    return cookies[ImmichCookie.OAuthCodeVerifier] || null;
  }

  async validateSharedLinkKey(key: string | string[]): Promise<AuthDto> {
    key = Array.isArray(key) ? key[0] : key;

    const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url');
    const sharedLink = await this.sharedLinkRepository.getByKey(bytes);
    if (!this.isValidSharedLink(sharedLink)) {
      throw new UnauthorizedException('Invalid share key');
    }

    return { user: sharedLink.user, sharedLink };
  }

  async validateSharedLinkSlug(slug: string | string[]): Promise<AuthDto> {
    slug = Array.isArray(slug) ? slug[0] : slug;

    const sharedLink = await this.sharedLinkRepository.getBySlug(slug);
    if (!this.isValidSharedLink(sharedLink)) {
      throw new UnauthorizedException('Invalid share slug');
    }

    return { user: sharedLink.user, sharedLink };
  }

  private isValidSharedLink(
    sharedLink?: AuthSharedLink & { user: AuthUser | null },

View on GitHub (pinned to f48d4b3321)