{"record":{"id":"3334301cb923d4df","repo":"immich-app/immich","slug":"invalid-share-slug","errorCode":null,"errorMessage":"Invalid share slug","messagePattern":"Invalid share slug","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"server/src/services/auth.service.ts","lineNumber":511,"sourceCode":"\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 },\n  ): sharedLink is AuthSharedLink & { user: AuthUser } {\n    return !!sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date());\n  }\n\n  private async validateApiKey(key: string): Promise<AuthDto> {\n    const hashed = this.cryptoRepository.hashSha256(key);\n    const apiKey = await this.apiKeyRepository.getKey(hashed);\n    if (apiKey?.user) {\n      return {\n        user: apiKey.user,\n        apiKey,","sourceCodeStart":493,"sourceCodeEnd":529,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/auth.service.ts#L493-L529","documentation":"Thrown by AuthService.validateSharedLinkSlug when a share link cannot be resolved or is no longer usable. The slug is looked up via sharedLinkRepository.getBySlug, then isValidSharedLink asserts the link exists, has a non-null user, and has not passed its expiresAt. If any of those fail the request is treated as unauthenticated.","triggerScenarios":"A request authenticated by a share-link slug (e.g. a public-album / shared-asset route that accepts ?slug= or a path segment) where the slug is unknown, was deleted, belongs to a link whose user was removed, or whose expiresAt is in the past.","commonSituations":"Stale bookmarks to a shared album whose link was regenerated or expired; copy-paste of a slug with a trailing space or missing segment; the owning user account was deleted leaving sharedLink.user null; clock skew where a link intended to be live is treated as expired.","solutions":["Verify the exact slug value against what is stored/generated by the server (no extra slashes, query params, or whitespace).","Check whether the shared link has expired (expiresAt) and regenerate it if needed.","Confirm the owning user still exists; recreate the share from a valid account if the user was deleted.","Catch UnauthorizedException at the controller edge and surface a 401 with a 'link invalid or expired' message to the client."],"exampleFix":"// before: blindly trust an unvalidated slug from a URL\nconst auth = await authService.validateSharedLinkSlug(req.query.slug);\n\n// after: normalize + presence-check, then handle the auth failure\nconst raw = Array.isArray(req.query.slug) ? req.query.slug[0] : req.query.slug;\nif (!raw || typeof raw !== 'string' || raw.trim().length === 0) {\n  throw new UnauthorizedException('Missing share slug');\n}\nlet auth: AuthDto;\ntry {\n  auth = await authService.validateSharedLinkSlug(raw.trim());\n} catch (e) {\n  if (e instanceof UnauthorizedException) {\n    throw new UnauthorizedException('Share link is invalid or expired');\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"const slug = Array.isArray(req.query.slug) ? req.query.slug[0] : req.query.slug;\nif (!slug || typeof slug !== 'string' || slug.trim().length === 0) {\n  throw new UnauthorizedException('A share slug is required');\n}","typeGuard":"function isShareSlug(v: unknown): v is string {\n  return typeof v === 'string' && v.trim().length > 0 && !/[/\\\\]/.test(v);\n}","tryCatchPattern":"try {\n  const auth = await authService.validateSharedLinkSlug(slug);\n} catch (e) {\n  if (e instanceof UnauthorizedException) {\n    // 401 to client: link unknown, expired, or orphaned\n    throw new UnauthorizedException('Share link is invalid or expired');\n  }\n  throw e;\n}","preventionTips":["Trim and length-check the slug before sending it to the auth service.","Regenerate share links with a sensible expiresAt and communicate expiry to consumers.","When deleting a user, invalidate that user's shared links to avoid orphaned slugs."],"tags":["auth","share-link","unauthorized","validation"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}