alibaba/canal · critical · CanalSinkException
state is not correct in transaction
Error message
state is not correct in transaction
What it means
Thrown in TimelineTransactionBarrier.clear(event) on the transaction-end branch: when isTransactionEnd(event) is true the code CAS-transitions txState from 1 (in-transaction) back to 0, and a failed CAS means another thread already altered txState. The transaction commit sequence (begin -> events -> end with matching clear calls) was violated, leaving the state machine inconsistent.
Source
Thrown at sink/src/main/java/com/alibaba/otter/canal/sink/entry/group/TimelineTransactionBarrier.java:72
}
}
public void clear(Event event) {
super.clear(event);
// 应该先判断2,再判断是否是事务尾,因为事务尾也可以导致txState的状态为2
// 如果先判断事务尾,那么2的状态可能永远没机会被修改了,系统出现死锁
// CanalSinkException被注释的代码是不是可以放开??我们内部使用的时候已经放开了,从代码逻辑的分析上以及实践效果来看,应该抛异常
if (txState.intValue() == 2) {// 非事务中
boolean result = txState.compareAndSet(2, 0);
if (result == false) {
throw new CanalSinkException("state is not correct in non-transaction");
}
} else if (isTransactionEnd(event)) {
inTransaction.set(false); // 事务结束并且已经成功写入store,清理标记,进入重新排队判断,允许新的事务进入
boolean result = txState.compareAndSet(1, 0);
if (result == false) {
throw new CanalSinkException("state is not correct in transaction");
}
}
}
protected boolean isPermit(Event event, long state) {
if (txState.intValue() == 1 && inTransaction.get()) { // 如果处于事务中,直接允许通过。因为事务头已经做过判断
return true;
} else if (txState.intValue() == 0) {
boolean result = super.isPermit(event, state);
if (result) {
// 可能第一条送过来的数据不为Begin,需要做判断处理,如果非事务,允许直接通过,比如DDL语句
if (isTransactionBegin(event)) {
if (txState.compareAndSet(0, 1)) {
inTransaction.set(true);
return true; // 事务允许通过
}
} else if (txState.compareAndSet(0, 2)) { // 非事务保护中
// 当基于zk-cursor启动的时候,拿到的第一个Event是TransactionEndView on GitHub (pinned to 87be50e876)
Solutions
- On this exception, restart the CanalInstance to reset txState and re-stream from the last good position — the barrier state cannot be safely recovered mid-flight.
- Verify binlog event ordering at the source (no gaps between TRANSACTIONBEGIN and TRANSACTIONEND entries).
- If triggered by HA failover, ensure the standby picks up cursor from ZooKeeper and the barrier reset path runs cleanly.
- Confirm the group sink thread count matches the configured groupSize to avoid duplicate transaction-end handling.
Defensive patterns
Strategy: fallback
Try / catch
try {
barrier.clear(event);
} catch (CanalSinkException e) {
if (e.getMessage().contains("in transaction")) {
logger.error("transaction state corrupted, restarting instance", e);
canalInstance.stop();
canalInstance.start();
} else {
throw e;
}
} Prevention
- Ensure binlog stream is complete and ordered (begin..end) — watch for parser gaps.
- On HA failover, let the reset() path run fully before new events enter the barrier.
- Match group sink thread count to groupSize to avoid duplicate transaction-end clears.
When it happens
Trigger: A transaction-end event's clear() runs while txState is no longer 1 (e.g. it was reset to 0 by an interrupt or by a concurrent clear); missing or duplicated transaction begin/end events from the parser; master-slave switch interrupting mid-transaction so reset() clears txState before the end's clear() arrives.
Common situations: Binlog parser emits incomplete transaction (end without matching begin, or reordered events); failover/HA switch causing an interrupt+reset between begin and end; multiple group sink threads both seeing the same transaction end.
Related errors
- state is not correct in non-transaction
- mq get/ack not support concurrent & async ack
- mq get/ack not support concurrent & async ack
- mq get/ack not support concurrent & async ack
- socket has Interrupted !
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/b66c59a8096750cb.
Report an issue: GitHub.