{"record":{"id":"6f4b4755f460df58","repo":"immich-app/immich","slug":"shared-link-is-not-password-protected","errorCode":null,"errorMessage":"Shared link is not password protected","messagePattern":"Shared link is not password protected","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/shared-link.service.ts","lineNumber":36,"sourceCode":"@Injectable()\nexport class SharedLinkService extends BaseService {\n  async getAll(auth: AuthDto, { id, albumId }: SharedLinkSearchDto): Promise<SharedLinkResponseDto[]> {\n    return this.sharedLinkRepository\n      .getAll({ userId: auth.user.id, id, albumId })\n\n      .then((links) => links.map((link) => mapSharedLink(link, { stripAssetMetadata: false })));\n  }\n\n  async login(auth: AuthDto, dto: SharedLinkLoginDto) {\n    if (!auth.sharedLink) {\n      throw new ForbiddenException();\n    }\n\n    const sharedLink = await this.findOrFail(auth.user.id, auth.sharedLink.id);\n    const { id, password } = sharedLink;\n\n    if (!password) {\n      throw new BadRequestException('Shared link is not password protected');\n    }\n\n    if (password !== dto.password) {\n      throw new UnauthorizedException('Invalid password');\n    }\n\n    return {\n      sharedLink: mapSharedLink(sharedLink, { stripAssetMetadata: !sharedLink.showExif }),\n      token: this.asToken({ id, password }),\n    };\n  }\n\n  async getMine(auth: AuthDto, authTokens: string[]) {\n    if (!auth.sharedLink) {\n      throw new ForbiddenException();\n    }\n\n    const sharedLink = await this.findOrFail(auth.user.id, auth.sharedLink.id);","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/shared-link.service.ts#L18-L54","documentation":"SharedLink.login authenticates a visitor against a password-protected shared link. If the link has no password stored, asking for a password login is invalid and a BadRequestException is thrown — the link is already openly accessible.","triggerScenarios":"POST to the shared-link access/login endpoint for a link whose sharedLink.password is null, i.e. submitting dto.password for a link that was created without a password.","commonSituations":"Client always shows a password prompt by default; stale client state where the link was edited to remove the password but the UI still asks for one; automated clients unconditionally posting a password.","solutions":["Skip the password step and access the shared link directly — no password is needed.","Refresh link metadata client-side to detect that password protection is off.","If a password is desired, have the owner add one via the shared link edit endpoint."],"exampleFix":"// before\nawait api.sharedLinkLogin(shareKey, { password }); // link has no password\n// after\nconst link = await api.getSharedLink(shareKey);\nif (link.requiresPassword) await api.sharedLinkLogin(shareKey, { password });","handlingStrategy":"validation","validationCode":"if (!linkData.requiresPassword) {\n  // open link: access directly, skip password login\n  return accessSharedLink(shareKey);\n}","typeGuard":"const needsPassword = (link) =>\n  typeof link === 'object' && link !== null && typeof link.hasPassword === 'boolean' && link.hasPassword;","tryCatchPattern":"try {\n  return await api.sharedLinkLogin(key, { password });\n} catch (e) {\n  if (e.status === 400 && /not password protected/.test(e.message)) {\n    return accessSharedLink(key); // no password needed\n  }\n  throw e;\n}","preventionTips":["Fetch link metadata first and only prompt for a password when required.","Refresh link state after owners edit protection settings.","Do not cache 'requires password' flags indefinitely."],"tags":["shared-link","password","bad-request","api"],"backgroundTag":"invalid-argument-value","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}