alibaba/canal · error · CanalServerException

ack error , clientId:%s batchId:%d is not exist , please che

Error message

ack error , clientId:%s batchId:%d is not exist , please check

What it means

Thrown by CanalServerWithEmbedded.ack() when MetaManager.removeBatch(clientIdentity, batchId) returns null. A null return means no batch with that batchId is registered for the client — it was already acked, already rolled back, or the batchId is wrong/stale. The server treats this as a duplicate or invalid ack and refuses.

Source

Thrown at server/src/main/java/com/alibaba/otter/canal/server/embedded/CanalServerWithEmbedded.java:403

    }

    /**
     * 进行 batch id 的确认。确认之后,小于等于此 batchId 的 Message 都会被确认。
     *
     * <pre>
     * 注意:进行反馈时必须按照batchId的顺序进行ack(需有客户端保证)
     * </pre>
     */
    @Override
    public void ack(ClientIdentity clientIdentity, long batchId) throws CanalServerException {
        checkStart(clientIdentity.getDestination());
        checkSubscribe(clientIdentity);

        CanalInstance canalInstance = canalInstances.get(clientIdentity.getDestination());
        PositionRange<LogPosition> positionRanges = null;
        positionRanges = canalInstance.getMetaManager().removeBatch(clientIdentity, batchId); // 更新位置
        if (positionRanges == null) { // 说明是重复的ack/rollback
            throw new CanalServerException(String.format("ack error , clientId:%s batchId:%d is not exist , please check",
                clientIdentity.getClientId(),
                batchId));
        }

        // 更新cursor最好严格判断下位置是否有跳跃更新
        // Position position = lastRollbackPostions.get(clientIdentity);
        // if (position != null) {
        // // Position position =
        // canalInstance.getMetaManager().getCursor(clientIdentity);
        // LogPosition minPosition =
        // CanalEventUtils.min(positionRanges.getStart(), (LogPosition)
        // position);
        // if (minPosition == position) {// ack的position要晚于该最后ack的位置,可能有丢数据
        // throw new CanalServerException(
        // String.format(
        // "ack error , clientId:%s batchId:%d %s is jump ack , last ack:%s",
        // clientIdentity.getClientId(), batchId, positionRanges,
        // position));

View on GitHub (pinned to 87be50e876)

Solutions

  1. Treat a duplicate/unknown-batch ack as idempotent success in client code — catch CanalServerException on ack and verify the batchId is no longer outstanding before retrying.
  2. Ensure ack is called exactly once per batchId from a single thread per clientId.
  3. After a client restart, do not re-ack old batchIds; call get() fresh after rollback.
  4. Check ZooKeeper health and the client identity consistency across restarts.

Example fix

// before: unconditional retry of ack
try {
    server.ack(clientId, batchId);
} catch (CanalServerException e) {
    server.ack(clientId, batchId); // wrong: repeats the failing call
}
// after: treat duplicate-ack as terminal
try {
    server.ack(clientId, batchId);
} catch (CanalServerException e) {
    if (e.getMessage().contains("is not exist")) {
        logger.warn("batch {} already acked or rolled back, ignoring", batchId);
    } else {
        throw e;
    }
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    server.ack(clientId, batchId);
} catch (CanalServerException e) {
    if (e.getMessage() != null && e.getMessage().contains("is not exist")) {
        // duplicate/late ack — already resolved, treat as success
        logger.warn("duplicate ack ignored for batch {}", batchId);
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling ack(clientIdentity, batchId) with a batchId that was already acknowledged; acking after a rollback of the same batch; acking a batchId that was never delivered; acking with a batchId from a different/restarted server instance where the MetaManager lost the batch record.

Common situations: At-least-once client that retries ack on timeout (duplicate ack); client restart that re-acks an id from before the restart; ZooKeeper metadata loss or session expiry clearing batch tracking; concurrent ack from two threads using the same clientId.

Related errors


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/13fd5de04ce52ba5. Report an issue: GitHub.