immich-app/immich · warning · BadRequestException

Partner already exists

Error message

Partner already exists

What it means

PartnerService.create rejects the request when a partner record for the exact pair (sharedById=caller, sharedWithId=target user) already exists, since the partner relation is unique per ordered pair. It throws BadRequestException before creating a duplicate row.

Solutions

  1. Check existing partners (GET /api/partners) before creating; treat an existing record as success.
  2. Use a different target user if a different share was intended.
  3. Delete the existing partner record (DELETE /api/partners/{id}) if it should be recreated.
  4. Wrap creation client-side to handle 400 'Partner already exists' as an idempotent no-op.

Example fix

// before
await api.partnersApi.createPartner({ sharedWithId: userId }); // throws if duplicate
// after
const existing = await api.partnersApi.getPartners({ direction: 'shared-by' });
if (!existing.some((p) => p.userId === userId)) {
  await api.partnersApi.createPartner({ sharedWithId: userId });
}
Defensive patterns

Strategy: validation

Validate before calling

const existing = await api.partnersApi.getPartners({ direction: 'shared-by' });
const alreadyPartner = existing.some((p) => p.userId === sharedWithId);
if (alreadyPartner) return; // idempotent no-op

Try / catch

try {
  await api.partnersApi.createPartner({ sharedWithId });
} catch (e) {
  if (e.status === 400 && String(e.message).includes('Partner already exists')) return; // treat as success
  throw e;
}

Prevention

When it happens

Trigger: POST /api/partners with a sharedWithId for which the calling user already has an existing partner record (either direction previously created).

Common situations: Double-clicking the share button in the UI, replaying an API request, sharing with the same user from two clients, or retrying after a timeout where the first request actually succeeded.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/955cf7765d772a70. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/partner.service.ts:16

import { BadRequestException, Injectable } from '@nestjs/common';
import { Partner } from 'src/database.js';
import { AuthDto } from 'src/dtos/auth.dto.js';
import { PartnerCreateDto, PartnerResponseDto, PartnerSearchDto, PartnerUpdateDto } from 'src/dtos/partner.dto.js';
import { mapUser } from 'src/dtos/user.dto.js';
import { Permission } from 'src/enum.js';
import { PartnerDirection, PartnerIds } from 'src/repositories/partner.repository.js';
import { BaseService } from 'src/services/base.service.js';

@Injectable()
export class PartnerService extends BaseService {
  async create(auth: AuthDto, { sharedWithId }: PartnerCreateDto): Promise<PartnerResponseDto> {
    const partnerId: PartnerIds = { sharedById: auth.user.id, sharedWithId };
    const exists = await this.partnerRepository.get(partnerId);
    if (exists) {
      throw new BadRequestException(`Partner already exists`);
    }

    const user = await this.userRepository.get(sharedWithId, {});
    if (!user) {
      this.logger.debug('Partner creation failed: user not found');
      throw new BadRequestException('Invalid user');
    }

    const partner = await this.partnerRepository.create(partnerId);
    return this.mapPartner(partner, PartnerDirection.SharedBy);
  }

  async remove(auth: AuthDto, sharedWithId: string): Promise<void> {
    const partnerId: PartnerIds = { sharedById: auth.user.id, sharedWithId };
    const partner = await this.partnerRepository.get(partnerId);
    if (!partner) {
      throw new BadRequestException('Partner not found');
    }

View on GitHub (pinned to e55ac299a4)