immich-app/immich · error · UnauthorizedException

Invalid share slug

Error message

Invalid share slug

What it means

Shared links can be addressed by a human-readable slug instead of a key. The service resolves the slug via sharedLinkRepository.getBySlug and applies the same validity check (exists, not expired). Failure yields UnauthorizedException.

Solutions

  1. Verify the exact slug with the owner and retry
  2. Regenerate the share link and use the new slug
  3. Ask the owner to extend the expiry
  4. Check whether the share was deleted or the album removed
Defensive patterns

Strategy: try-catch

Try / catch

catch (e) { if (e.status === 401 && e.message === 'Invalid share slug') { /* prompt for fresh link */ } }

Prevention

When it happens

Trigger: GET /share/<slug> where the slug does not match any existing share link, or the matching link has expired or been revoked.

Common situations: Typos in the slug; slug reused/renamed by the owner; share deleted after album/asset was unshared; expired link still bookmarked.

Related errors


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

Appendix: source

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

  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 },
  ): sharedLink is AuthSharedLink & { user: AuthUser } {
    return !!sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date());
  }

  private async validateApiKey(key: string): Promise<AuthDto> {
    const hashed = this.cryptoRepository.hashSha256(key);
    const apiKey = await this.apiKeyRepository.getKey(hashed);
    if (apiKey?.user) {
      return {
        user: apiKey.user,
        apiKey,

View on GitHub (pinned to f48d4b3321)