{"record":{"id":"004b29393dd69bf5","repo":"immich-app/immich","slug":"syncrequesttype-albumassetsv1-is-deprecated-use-s","errorCode":null,"errorMessage":"SyncRequestType.AlbumAssetsV1 is deprecated, use SyncRequestType.AlbumAssetsV2 instead","messagePattern":"SyncRequestType\\.AlbumAssetsV1 is deprecated, use SyncRequestType\\.AlbumAssetsV2 instead","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/sync.service.ts","lineNumber":577,"sourceCode":"\n        await sendEntityBackfillCompleteAck(response, backfillType, createId);\n      }\n    } else if (albums.length > 0) {\n      await this.upsertBackfillCheckpoint({\n        type: backfillType,\n        sessionId,\n        createId: albums.at(-1)!.createId,\n      });\n    }\n\n    const upserts = this.syncRepository.albumUser.getUpserts({ ...options, ack: checkpointMap[upsertType] });\n    for await (const { updateId, ...data } of upserts) {\n      await send(response, { type: upsertType, ids: [updateId], data });\n    }\n  }\n\n  private syncAlbumAssetsV1(): Promise<void> {\n    throw new BadRequestException(\n      'SyncRequestType.AlbumAssetsV1 is deprecated, use SyncRequestType.AlbumAssetsV2 instead',\n    );\n  }\n\n  private async syncAlbumAssetsV2(\n    options: SyncQueryOptions,\n    response: Writable,\n    checkpointMap: CheckpointMap,\n    sessionId: string,\n  ) {\n    const backfillType = SyncEntityType.AlbumAssetBackfillV2;\n    const backfillCheckpoint = checkpointMap[backfillType];\n    const albums = await this.syncRepository.album.getCreatedAfter({\n      ...options,\n      afterCreateId: backfillCheckpoint?.updateId,\n    });\n    const updateType = SyncEntityType.AlbumAssetUpdateV2;\n    const createType = SyncEntityType.AlbumAssetCreateV2;","sourceCodeStart":559,"sourceCodeEnd":595,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/sync.service.ts#L559-L595","documentation":"Generic deprecation guard: syncAlbumAssetsV1 unconditionally throws for sync requests using the legacy SyncRequestType.AlbumAssetsV1 protocol, forcing clients onto AlbumAssetsV2 which uses backfill checkpoints (sendEntityBackfillCompleteAck / upsertBackfillCheckpoint) instead of the old ack scheme. The error is a protocol-cutover signal, not a data problem; the at-fault input is the deprecated SyncRequestType.AlbumAssetsV1 query parameter.","triggerScenarios":"Client includes SyncRequestType.AlbumAssetsV1 in its sync request; handlers dispatches to syncAlbumAssetsV1 which always throws.","commonSituations":"Album-heavy workflows on old mobile clients after a server upgrade.","solutions":["Update the Immich mobile app to an AlbumAssetsV2-capable version","Replace AlbumAssetsV1 with SyncRequestType.AlbumAssetsV2 in the sync request"],"exampleFix":"// before\nrequests: [{ type: SyncRequestType.AlbumAssetsV1 }]\n// after\nrequests: [{ type: SyncRequestType.AlbumAssetsV2 }]","handlingStrategy":"validation","validationCode":"if (requests.some(r => r.type === SyncRequestType.AlbumAssetsV1)) throw new Error('Use AlbumAssetsV2');","typeGuard":"null","tryCatchPattern":"try {\n  await syncStream(requests);\n} catch (e) {\n  if (e.status === 400 && e.message.includes('AlbumAssetsV1')) {\n    requests = requests.map(r => r.type === 'AlbumAssetsV1' ? { ...r, type: 'AlbumAssetsV2' } : r);\n  }\n}","preventionTips":["Use AlbumAssetsV2 for album membership sync","Update clients before upgrading the server","Remove V1 types from persisted sync request lists"],"tags":["sync","deprecated","api-version","albums"],"backgroundTag":"deprecated-api-usage","analyzedSha":"f48d4b332127ad365ba256108799ca8f571d2dd5","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}