{"record":{"id":"c22811fb167fdb94","repo":"immich-app/immich","slug":"invalid-share-key","errorCode":null,"errorMessage":"Invalid share key","messagePattern":"Invalid share key","errorType":"exception","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"server/src/services/auth.service.ts","lineNumber":504,"sourceCode":"  }\n\n  private getCookieOauthState(headers: IncomingHttpHeaders): string | null {\n    const cookies = parse(headers.cookie || '');\n    return cookies[ImmichCookie.OAuthState] || null;\n  }\n\n  private getCookieCodeVerifier(headers: IncomingHttpHeaders): string | null {\n    const cookies = parse(headers.cookie || '');\n    return cookies[ImmichCookie.OAuthCodeVerifier] || null;\n  }\n\n  async validateSharedLinkKey(key: string | string[]): Promise<AuthDto> {\n    key = Array.isArray(key) ? key[0] : key;\n\n    const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url');\n    const sharedLink = await this.sharedLinkRepository.getByKey(bytes);\n    if (!this.isValidSharedLink(sharedLink)) {\n      throw new UnauthorizedException('Invalid share key');\n    }\n\n    return { user: sharedLink.user, sharedLink };\n  }\n\n  async validateSharedLinkSlug(slug: string | string[]): Promise<AuthDto> {\n    slug = Array.isArray(slug) ? slug[0] : slug;\n\n    const sharedLink = await this.sharedLinkRepository.getBySlug(slug);\n    if (!this.isValidSharedLink(sharedLink)) {\n      throw new UnauthorizedException('Invalid share slug');\n    }\n\n    return { user: sharedLink.user, sharedLink };\n  }\n\n  private isValidSharedLink(\n    sharedLink?: AuthSharedLink & { user: AuthUser | null },","sourceCodeStart":486,"sourceCodeEnd":522,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/auth.service.ts#L486-L522","documentation":"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.","triggerScenarios":"GET with ?key=... where the key is malformed, deleted, revoked, or the share link expired (expiresAt in the past).","commonSituations":"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.","solutions":["Regenerate the share link from the owner account and use the fresh key","Verify the full key was copied (no truncation, correct URL-encoding)","Have the owner extend or remove expiresAt on the share link","Check the server database/backup to confirm the key still exists"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const isPlausibleKey = (k: string) => k.length === 100 || /^[A-Za-z0-9_-]+$/.test(k);","tryCatchPattern":"catch (e) { if (e.status === 401 && e.message === 'Invalid share key') { /* regenerate link */ } }","preventionTips":["Copy full URLs without truncation","Track link expiry and regenerate proactively","Re-create links after migrations"],"tags":["auth","shared-links","unauthorized"],"backgroundTag":"invalid-credentials","analyzedSha":"f48d4b332127ad365ba256108799ca8f571d2dd5","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}