immich-app/immich · warning · UnauthorizedException

Password required

Error message

Password required

What it means

In SharedLinkService.getMine(), when the resolved shared link HAS a password, the caller must present a valid auth token computed from {id, password}. If none of the request's authTokens match asToken({id, password}), UnauthorizedException 'Password required' is thrown (shared-link.service.ts:58, HTTP 401).

Source

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

      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);
    const { id, password } = sharedLink;

    if (password && !authTokens.includes(this.asToken({ id, password }))) {
      throw new UnauthorizedException('Password required');
    }

    return mapSharedLink(sharedLink, { stripAssetMetadata: !sharedLink.showExif });
  }

  async get(auth: AuthDto, id: string): Promise<SharedLinkResponseDto> {
    const sharedLink = await this.findOrFail(auth.user.id, id);
    return mapSharedLink(sharedLink, { stripAssetMetadata: false });
  }

  async create(auth: AuthDto, dto: SharedLinkCreateDto): Promise<SharedLinkResponseDto> {
    switch (dto.type) {
      case SharedLinkType.Album: {
        if (!dto.albumId) {
          throw new BadRequestException('Invalid albumId');
        }
        await this.requireAccess({ auth, permission: Permission.AlbumShare, ids: [dto.albumId] });
        break;

View on GitHub (pinned to 199723261c)

Solutions

  1. Complete the shared-link login() flow first; the returned token is what authorizes getMine.
  2. Ensure the shared-link auth token/cookie is sent with the getMine request.
  3. If the owner changed the password, re-run login to obtain a fresh token.

Example fix

// before - skip login
fetch('/shared-links/me', { headers: { 'x-immich-share-key': key } });
// after - login first, then read
const { token } = await sharedLinkApi.login({ password });
fetch('/shared-links/me', { headers: { 'x-immich-share-key': key, 'x-immich-share-token': token } });
Defensive patterns

Strategy: validation

Validate before calling

// Ensure a shared-link auth token exists before calling getMine.
let token = getShareAuthToken();
if (!token) {
  const res = await sharedLinkApi.login({ password });
  token = res.token;
  storeShareAuthToken(token);
}
await sharedLinkApi.getMine({ shareKey, token });

Type guard

const hasShareAuthToken = (tokens: string[] | undefined): boolean =>
  Array.isArray(tokens) && tokens.length > 0;

Try / catch

try {
  await sharedLinkApi.getMine();
} catch (e) {
  if (e instanceof UnauthorizedException && /password required/i.test(e.message)) {
    // run login flow, then retry getMine
    const { token } = await sharedLinkApi.login({ password });
    return retryGetMineWith(token);
  } else throw e;
}

Prevention

When it happens

Trigger: Accessing the shared-link viewer (getMine) for a password-protected link without first completing the login flow that mints the shared-link auth cookie/token. The token is only issued by a successful login().

Common situations: Loading the share URL directly without the password cookie set, the auth cookie expiring or being cleared, or the password having changed since the token was issued.

Related errors


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