immich-app/immich · error · BadRequestException

Invalid ack type

Error message

Invalid ack type: ${type}

What it means

setAcks accepts client acknowledgements for known sync entity types, but there is no class-validator on the payload (per the TODO), so it manually checks the type against the SyncEntityType enum and throws BadRequestException for unknown values.

Solutions

  1. Update the Immich client to match the server version so SyncEntityType values align
  2. Inspect the ack payload and remove/correct entries whose type is not a valid SyncEntityType
  3. If stale, trigger a sync reset so the client rebuilds its ack state

Example fix

// before
await setAcks([{ type: 'AssetV1', ack: '...' }])
// after
await setAcks([{ type: SyncEntityType.AssetV1, ack: '...' }])
Defensive patterns

Strategy: validation

Validate before calling

const validTypes = Object.values(SyncEntityType);
const isValidAck = (a) => validTypes.includes(a.type);
acks.forEach(a => { if (!isValidAck(a)) throw new Error(`Invalid ack type: ${a.type}`); });

Type guard

const isValidSyncEntityType = (t) => Object.values(SyncEntityType).includes(t);

Try / catch

try {
  await syncClient.setAcks(acks);
} catch (e) {
  if (e.status === 400 && e.message.startsWith('Invalid ack type')) {
    acks = acks.filter(a => isValidSyncEntityType(a.type)); // drop stale types
  }
}

Prevention

When it happens

Trigger: POSTing acks to the sync ack endpoint containing a type string not present in SyncEntityType (typo, newer client on older server, fabricated type). Special case: SyncResetV1 is handled before this check and resets progress instead of being recorded.

Common situations: Version mismatch between mobile client and server after downgrade; hand-crafted API requests; corrupted local ack database sending stale/invalid types.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

    return this.syncCheckpointRepository.getAll(sessionId);
  }

  async setAcks(auth: AuthDto, dto: SyncAckSetDto) {
    const sessionId = auth.session?.id;
    if (!sessionId) {
      return throwSessionRequired();
    }

    const checkpoints: Record<string, Insertable<SessionSyncCheckpointTable>> = {};
    for (const ack of dto.acks) {
      const { type } = fromAck(ack);
      if (type === SyncEntityType.SyncResetV1) {
        await this.sessionRepository.resetSyncProgress(sessionId);
        return;
      }
      // TODO proper ack validation via class validator
      if (!Object.values(SyncEntityType).includes(type)) {
        throw new BadRequestException(`Invalid ack type: ${type}`);
      }

      // TODO pick the latest ack for each type, instead of using the last one
      checkpoints[type] = { sessionId, type, ack };
    }

    await this.syncCheckpointRepository.upsertAll(Object.values(checkpoints));
  }

  async deleteAcks(auth: AuthDto, dto: SyncAckDeleteDto) {
    const sessionId = auth.session?.id;
    if (!sessionId) {
      return throwSessionRequired();
    }

    await this.syncCheckpointRepository.deleteAll(sessionId, dto.types);
  }

View on GitHub (pinned to f48d4b3321)