immich-app/immich · warning · BadRequestException
Shared link is not password protected
Error message
Shared link is not password protected
What it means
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.
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.
Example fix
// before
await api.sharedLinkLogin(shareKey, { password }); // link has no password
// after
const link = await api.getSharedLink(shareKey);
if (link.requiresPassword) await api.sharedLinkLogin(shareKey, { password }); Defensive patterns
Strategy: validation
Validate before calling
if (!linkData.requiresPassword) {
// open link: access directly, skip password login
return accessSharedLink(shareKey);
} Type guard
const needsPassword = (link) => typeof link === 'object' && link !== null && typeof link.hasPassword === 'boolean' && link.hasPassword;
Try / catch
try {
return await api.sharedLinkLogin(key, { password });
} catch (e) {
if (e.status === 400 && /not password protected/.test(e.message)) {
return accessSharedLink(key); // no password needed
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid license key
- Invalid password
- Not in maintenance mode
- Password required
- This endpoint can only be used with a session token
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/6f4b4755f460df58.
Report an issue: GitHub.
Appendix: 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 e55ac299a4)