alibaba/canal · error · CanalServerException
rollback error, clientId:%s batchId:%d is not exist , please
Error message
rollback error, clientId:%s batchId:%d is not exist , please check
What it means
Thrown by CanalServerWithEmbedded.rollback(ClientIdentity, long batchId) when MetaManager.removeBatch(clientIdentity, batchId) returns null — the batchId is not registered (already acked, already rolled back, or never existed). Note the no-arg rollback path earlier in the method returns early if the client has no subscription, so this only fires for the batchId-specific rollback of a known-subscribed client.
Source
Thrown at server/src/main/java/com/alibaba/otter/canal/server/embedded/CanalServerWithEmbedded.java:480
/**
* 回滚到未进行 {@link #ack} 的地方,下次fetch的时候,可以从最后一个没有 {@link #ack} 的地方开始拿
*/
@Override
public void rollback(ClientIdentity clientIdentity, Long batchId) throws CanalServerException {
checkStart(clientIdentity.getDestination());
CanalInstance canalInstance = canalInstances.get(clientIdentity.getDestination());
// 因为存在第一次链接时自动rollback的情况,所以需要忽略未订阅
boolean hasSubscribe = canalInstance.getMetaManager().hasSubscribe(clientIdentity);
if (!hasSubscribe) {
return;
}
synchronized (canalInstance) {
// 清除batch信息
PositionRange<LogPosition> positionRanges = canalInstance.getMetaManager().removeBatch(clientIdentity,
batchId);
if (positionRanges == null) { // 说明是重复的ack/rollback
throw new CanalServerException(String.format("rollback error, clientId:%s batchId:%d is not exist , please check",
clientIdentity.getClientId(),
batchId));
}
// lastRollbackPostions.put(clientIdentity,
// positionRanges.getEnd());// 记录一下最后rollback的位置
// TODO 后续rollback到指定的batchId位置
canalInstance.getEventStore().rollback();// rollback
// eventStore中的状态信息
logger.info("rollback successfully, clientId:{} batchId:{} position:{}",
clientIdentity.getClientId(),
batchId,
positionRanges);
}
}
public Map<String, CanalInstance> getCanalInstances() {
return Maps.newHashMap(canalInstances);View on GitHub (pinned to 87be50e876)
Solutions
- Treat an unknown-batch rollback as idempotent — catch CanalServerException mentioning 'is not exist' and log/continue rather than retry.
- Guarantee rollback is invoked once per batchId and never after a successful ack of the same batch.
- After restart, issue a no-arg rollback to reset cursor state instead of targeting a stale batchId.
- Audit concurrent client threads sharing a clientId to prevent racing ack/rollback.
Example fix
// before: rollback then retry blindly
try {
server.rollback(clientId, batchId);
} catch (CanalServerException e) {
server.rollback(clientId, batchId);
}
// after: idempotent handling of stale batch
try {
server.rollback(clientId, batchId);
} catch (CanalServerException e) {
if (!e.getMessage().contains("is not exist")) throw e;
logger.warn("batch {} no longer tracked, rollback already applied", batchId);
} Defensive patterns
Strategy: try-catch
Try / catch
try {
server.rollback(clientId, batchId);
} catch (CanalServerException e) {
if (e.getMessage() != null && e.getMessage().contains("is not exist")) {
logger.warn("duplicate rollback ignored for batch {}", batchId);
return;
}
throw e;
} Prevention
- Do not rollback a batchId that was already acked.
- On restart use no-arg rollback to reset cursor instead of targeting stale ids.
- Serialize ack/rollback per clientId to prevent races.
When it happens
Trigger: Calling rollback(clientIdentity, batchId) with a batchId that was already acked or already rolled back; rolling back a batchId never delivered by get(); rolling back after the batch metadata was cleared by a server restart or ZooKeeper session loss.
Common situations: Client retry logic that re-rolls back on failure (duplicate rollback); ack already happened then a late rollback arrives; client restart re-issuing rollback for a stale batchId.
Related errors
- ack error , clientId:%s batchId:%d is not exist , please che
- apply failed
- not support for memory
- clientId:%s has last batch:[%s] isn't ack , maybe loss data
- ClientIdentity:%s should subscribe first
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/e9cc2effe78c4e5e.
Report an issue: GitHub.