immich-app/immich · warning · BadRequestException

Shared link is not password protected

Error message

Shared link is not password protected

What it means

In SharedLinkService.login(), after the shared link is resolved, if it has no password set the service throws BadRequestException 'Shared link is not password protected' (shared-link.service.ts:36, HTTP 400). Logging into a link that requires no password is a category error.

Source

Thrown at server/src/services/shared-link.service.ts:36

@Injectable()
export class SharedLinkService extends BaseService {
  async getAll(auth: AuthDto, { id, albumId }: SharedLinkSearchDto): Promise<SharedLinkResponseDto[]> {
    return this.sharedLinkRepository
      .getAll({ userId: auth.user.id, id, albumId })

      .then((links) => links.map((link) => mapSharedLink(link, { stripAssetMetadata: false })));
  }

  async login(auth: AuthDto, dto: SharedLinkLoginDto) {
    if (!auth.sharedLink) {
      throw new ForbiddenException();
    }

    const sharedLink = await this.findOrFail(auth.user.id, auth.sharedLink.id);
    const { id, password } = sharedLink;

    if (!password) {
      throw new BadRequestException('Shared link is not password protected');
    }

    if (password !== dto.password) {
      throw new UnauthorizedException('Invalid password');
    }

    return {
      sharedLink: mapSharedLink(sharedLink, { stripAssetMetadata: !sharedLink.showExif }),
      token: this.asToken({ id, password }),
    };
  }

  async getMine(auth: AuthDto, authTokens: string[]) {
    if (!auth.sharedLink) {
      throw new ForbiddenException();
    }

    const sharedLink = await this.findOrFail(auth.user.id, auth.sharedLink.id);

View on GitHub (pinned to 199723261c)

Solutions

  1. Before showing a password prompt or calling login, check the shared link's password-protected flag from getMine/get and skip login for unprotected links.
  2. If the link was meant to be protected, set a password via PATCH /shared-links/:id (update).
  3. Clear client-side state that assumes a password flow for this link.

Example fix

// before - always login
await sharedLinkApi.login({ password });
// after - only when protected
if (sharedLink.password) {
  await sharedLinkApi.login({ password });
}
Defensive patterns

Strategy: validation

Validate before calling

// Resolve the link first; only login if it is password-protected.
const link = await sharedLinkApi.getMine();
if (!link.password) {
  // unprotected link — no login needed, proceed directly
  return link;
}
return sharedLinkApi.login({ password });

Type guard

const isPasswordProtected = (link: { password?: string | null }): boolean => !!link.password;

Try / catch

try {
  await sharedLinkApi.login(dto);
} catch (e) {
  if (e instanceof BadRequestException && /not password protected/i.test(e.message)) {
    // skip login; this link needs no password
    return sharedLinkApi.getMine();
  } else throw e;
}

Prevention

When it happens

Trigger: POST to the shared-link login endpoint for a shared link whose password field is null/empty. The caller should not attempt a password exchange for an unprotected link.

Common situations: Frontend always invoking login regardless of whether the link is password-protected, stale client state assuming a password prompt, or a link whose password was cleared via update but the client still tries to log in.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/6f4b4755f460df58. Report an issue: GitHub.