alibaba/canal · error · CanalParseException
ERROR ## the bfColumns is null
Error message
ERROR ## the bfColumns is null
What it means
Thrown from isUpdate() when the bfColumns parameter (the 'before' image columns list for an UPDATE event) is null. The method needs to compare before/after column values to determine which columns changed, so a null before-list is an unrecoverable internal state error.
Source
Thrown at parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/dbsync/LogEventConvert.java:964
}
// add current gtid last committed
if (StringUtils.isNotEmpty(logHeader.getCurrentGtidLastCommit())) {
Pair pair = createSpecialPair("curtGtidLct", logHeader.getCurrentGtidLastCommit());
headerBuilder.addProps(pair);
}
// add rowsCount suppport
if (rowsCount > 0) {
Pair pair = createSpecialPair("rowsCount", String.valueOf(rowsCount));
headerBuilder.addProps(pair);
}
return headerBuilder.build();
}
private boolean isUpdate(List<Column> bfColumns, String newValue, int index) {
if (bfColumns == null) {
throw new CanalParseException("ERROR ## the bfColumns is null");
}
if (index < 0) {
return false;
}
for (Column column : bfColumns) {
if (column.getIndex() == index) {// 比较before / after的column index
if (column.getIsNull() && newValue == null) {
// 如果全是null
return false;
} else if (newValue != null && (!column.getIsNull() && column.getValue().equals(newValue))) {
// fixed issue #135, old column is Null
// 如果不为null,并且相等
return false;
}
}
}View on GitHub (pinned to 87be50e876)
Solutions
- Set binlog_row_image=FULL on the MySQL server: SET GLOBAL binlog_row_image='FULL'; to ensure complete before/after images.
- Upgrade Canal — newer versions handle MINIMAL row image without calling isUpdate on null bfColumns.
- If you see this consistently, report it as a Canal bug with the binlog_row_image setting and event details.
Example fix
-- before mysql> SET GLOBAL binlog_row_image = 'MINIMAL'; -- after mysql> SET GLOBAL binlog_row_image = 'FULL'; -- add to my.cnf for persistence: -- [mysqld] -- binlog_row_image=FULL
Defensive patterns
Strategy: try-catch
Try / catch
// isUpdate is internal; the outer parseRowsEvent catch handles it:
try {
Entry entry = parseRowsEvent(event);
} catch (CanalParseException e) {
if (e.getMessage().contains("bfColumns is null")) {
// Likely binlog_row_image=MINIMAL — log and report
logger.error("bfColumns null — check binlog_row_image setting on MySQL", e);
// This is likely a Canal bug; report upstream
}
} Prevention
- Set binlog_row_image=FULL on the MySQL server to ensure complete before-images for UPDATE events.
- Keep Canal updated — newer versions handle MINIMAL row image mode correctly.
- Report consistent occurrences of this error as a Canal bug with server version and binlog_row_image setting.
When it happens
Trigger: isUpdate(bfColumns, newValue, index) is called with bfColumns == null. This happens in the UPDATE row-parsing path when the before-image columns were not populated — typically because the binlog event's before-image is empty (binlog_row_image=MINIMAL strips the before-image for unchanged columns) and the code path didn't handle the null case before calling isUpdate.
Common situations: binlog_row_image is set to MINIMAL on the MySQL server, which strips the before-image for UPDATE events, causing bfColumns to be null when Canal expects a full before-image. This is an internal Canal bug or a configuration mismatch between expected and actual binlog_row_image. Rare in practice with modern Canal versions.
Related errors
- No such method: '{}' @ {}
- No such field: '{}' @ {}
- Unable to unwrap {} to com.mysql.jdbc.ConnectionImpl
- Get null field:{}#io
- Unable to load com.mysql.jdbc.ConnectionImpl
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/b9a234412b187b18.
Report an issue: GitHub.