immich-app/immich · error · BadRequestException

SyncRequestType.AssetsV1 is deprecated, use…

Error message

SyncRequestType.AssetsV1 is deprecated, use SyncRequestType.AssetsV2 instead

What it means

The AssetsV1 sync request type is removed; the handler is a stub that immediately throws BadRequestException directing callers to AssetsV2. Server refuses to serve the deprecated v1 asset sync stream.

Solutions

  1. Update the Immich mobile app to a version supporting SyncRequestType.AssetsV2
  2. Remove AssetsV1 from the client's sync request list and use AssetsV2
  3. Downgrade the server only as a temporary measure (not recommended)

Example fix

// before
requests: [{ type: SyncRequestType.AssetsV1 }]
// after
requests: [{ type: SyncRequestType.AssetsV2 }]
Defensive patterns

Strategy: validation

Validate before calling

const SUPPORTED = new Set(Object.values(SyncRequestType).filter(t => t.endsWith('V2')));
if (requests.some(r => !SUPPORTED.has(r.type))) throw new Error('V1 sync types are deprecated; use V2');

Type guard

null

Try / catch

try {
  await syncStream(requests);
} catch (e) {
  if (e.status === 400 && e.message.includes('deprecated, use')) {
    requests = requests.map(r => upgradeToV2(r.type));
  }
}

Prevention

When it happens

Trigger: A client includes SyncRequestType.AssetsV1 in its sync request list; handlers dispatches to syncAssetsV1 which always throws.

Common situations: Older Immich mobile app (pre AssetsV2) syncing against a newer server; cached sync request types not refreshed after server upgrade.

Understand the failure class

Background: "is deprecated and will be removed" — deprecation warnings for old API names, keywords, and options, and how to migrate before the removal release — this error's family across 29 libraries.

Related errors


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

Appendix: source

Thrown at server/src/services/sync.service.ts:327

    }
  }

  private async syncPartnersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {
    const deleteType = SyncEntityType.PartnerDeleteV1;
    const deletes = this.syncRepository.partner.getDeletes({ ...options, ack: checkpointMap[deleteType] });
    for await (const { id, ...data } of deletes) {
      await send(response, { type: deleteType, ids: [id], data });
    }

    const upsertType = SyncEntityType.PartnerV1;
    const upserts = this.syncRepository.partner.getUpserts({ ...options, ack: checkpointMap[upsertType] });
    for await (const { updateId, ...data } of upserts) {
      await send(response, { type: upsertType, ids: [updateId], data });
    }
  }

  private syncAssetsV1(): Promise<void> {
    throw new BadRequestException('SyncRequestType.AssetsV1 is deprecated, use SyncRequestType.AssetsV2 instead');
  }

  private async syncAssetsV2(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) {
    const deleteType = SyncEntityType.AssetDeleteV1;
    const deletes = this.syncRepository.asset.getDeletes({ ...options, ack: checkpointMap[deleteType] });
    for await (const { id, ...data } of deletes) {
      await send(response, { type: deleteType, ids: [id], data });
    }

    const upsertType = SyncEntityType.AssetV2;
    const upserts = this.syncRepository.asset.getUpserts({ ...options, ack: checkpointMap[upsertType] });
    for await (const { updateId, ...data } of upserts) {
      await send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV2(data) });
    }
  }

  private syncPartnerAssetsV1(): Promise<void> {
    throw new BadRequestException(

View on GitHub (pinned to f48d4b3321)