{"record":{"id":"f8a3651b9c5fa476","repo":"immich-app/immich","slug":"invalid-ack-type-type","errorCode":null,"errorMessage":"Invalid ack type: ${type}","messagePattern":"Invalid ack type: (.+?)","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/sync.service.ts","lineNumber":113,"sourceCode":"    return this.syncCheckpointRepository.getAll(sessionId);\n  }\n\n  async setAcks(auth: AuthDto, dto: SyncAckSetDto) {\n    const sessionId = auth.session?.id;\n    if (!sessionId) {\n      return throwSessionRequired();\n    }\n\n    const checkpoints: Record<string, Insertable<SessionSyncCheckpointTable>> = {};\n    for (const ack of dto.acks) {\n      const { type } = fromAck(ack);\n      if (type === SyncEntityType.SyncResetV1) {\n        await this.sessionRepository.resetSyncProgress(sessionId);\n        return;\n      }\n      // TODO proper ack validation via class validator\n      if (!Object.values(SyncEntityType).includes(type)) {\n        throw new BadRequestException(`Invalid ack type: ${type}`);\n      }\n\n      // TODO pick the latest ack for each type, instead of using the last one\n      checkpoints[type] = { sessionId, type, ack };\n    }\n\n    await this.syncCheckpointRepository.upsertAll(Object.values(checkpoints));\n  }\n\n  async deleteAcks(auth: AuthDto, dto: SyncAckDeleteDto) {\n    const sessionId = auth.session?.id;\n    if (!sessionId) {\n      return throwSessionRequired();\n    }\n\n    await this.syncCheckpointRepository.deleteAll(sessionId, dto.types);\n  }\n","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/sync.service.ts#L95-L131","documentation":"Thrown (as BadRequestException) in SyncService.setAcks when an ack decoded by fromAck(ack) yields a type that is not a member of the SyncEntityType enum. It is a placeholder guard (the TODO notes class-validator validation is planned) that rejects unknown ack types before they reach the checkpoint upsert.","triggerScenarios":"POST /sync/acks with a body whose acks[].type string does not match any SyncEntityType value, e.g. an old client sending a renamed/removed entity type, or a hand-crafted payload.","commonSituations":"Client/server version skew (server upgraded, client still emits a v1 ack); typo in a custom integration; SDK regenerated against a newer enum than the deployed server.","solutions":["Upgrade the client to match the server's SyncEntityType values; re-fetch the openapi schema.","Drop unknown ack types on the client before submitting (filter against the documented enum).","Pin client and server to the same Immich version until ack types stabilize.","Inspect the request body; the failing type is echoed back in the message."],"exampleFix":"// before\nawait api.syncAcks([{ type: 'AssetV1', ack: '...' }]);\n// after\nawait api.syncAcks([{ type: 'AssetV2', ack: '...' }]);","handlingStrategy":"validation","validationCode":"import { SyncEntityType } from './openapi';\nconst validTypes = new Set(Object.values(SyncEntityType));\nconst safeAcks = dto.acks.filter(a => validTypes.has(fromAck(a).type));\nawait syncApi.setAcks({ acks: safeAcks });","typeGuard":"function isKnownSyncEntityType(t: unknown): t is SyncEntityType {\n  return typeof t === 'string' && Object.values(SyncEntityType).includes(t as SyncEntityType);\n}","tryCatchPattern":"try { await syncApi.setAcks({ acks }); }\ncatch (e) {\n  if (e instanceof BadRequestException && /Invalid ack type/.test(e.message)) {\n    // re-fetch enum, filter unknown acks, retry once\n  }\n}","preventionTips":["Pin client and server Immich versions together.","Regenerate the SDK from the deployed openapi after each server upgrade.","Validate ack types client-side against the generated enum before submit."],"tags":["sync","validation","version-skew","bad-request"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}