alibaba/canal · error · IllegalStateException

unexpected binlog image query result: + rs.getFieldValues()

Error message

unexpected binlog image query result: + rs.getFieldValues()

What it means

In loadBinlogImage(), the field-count guard tolerates missing binlog_row_image (old versions default to FULL), but the final check throws IllegalStateException when binlogFormat == null. Note this is a latent bug: the guard checks binlogFormat (a field set by loadBinlogFormat) instead of binlogImage, so this fires when the binlog format was never loaded — typically when loadBinlogImage runs but loadBinlogFormat did not run first or failed silently.

Solutions

  1. Ensure loadBinlogFormat() runs and succeeds before loadBinlogImage() (updateSettings() is the normal entry that sequences these).
  2. Confirm the account can read `show variables like 'binlog_format'` so binlogFormat is populated.
  3. If patching canal, fix the guard at line 556 to check `binlogImage == null` instead of `binlogFormat == null` — the current condition is a copy-paste defect.

Example fix

// defect at MysqlConnection.java:556 — checks the wrong field
if (binlogFormat == null) {
    throw new IllegalStateException("unexpected binlog image query result:" + rs.getFieldValues());
}
// corrected guard:
if (binlogImage == null) {
    throw new IllegalStateException("unexpected binlog image query result:" + rs.getFieldValues());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure loadBinlogFormat succeeded before loadBinlogImage runs
if (connection.getBinlogFormat() == null) {
    throw new IllegalStateException("binlogFormat not loaded; cannot safely load binlog image");
}

Try / catch

try {
    connection.loadBinlogFormat();
    connection.loadBinlogImage();
} catch (IllegalStateException e) {
    if (e.getMessage().contains("unexpected binlog image query result")) {
        logger.error("binlogImage guard fired on null binlogFormat (known copy-paste defect); ensure loadBinlogFormat ran first", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: loadBinlogImage() executes and binlogFormat is still null — either loadBinlogFormat was skipped, threw and was swallowed, or the field was reset. The error message misleadingly blames the image query result.

Common situations: Custom call ordering that invokes loadBinlogImage() without a preceding successful loadBinlogFormat(); a prior loadBinlogFormat() that set binlogFormat=null via an unrecognized value (though that path throws first); test wiring that constructs the connection without full initialization.

Related errors


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

Appendix: source

Thrown at parse/src/main/java/com/alibaba/otter/canal/parse/inbound/mysql/MysqlConnection.java:557

     */
    private void loadBinlogImage() {
        ResultSetPacket rs = null;
        try {
            rs = query("show variables like 'binlog_row_image'");
        } catch (IOException e) {
            throw new CanalParseException(e);
        }

        List<String> columnValues = rs.getFieldValues();
        if (columnValues == null || columnValues.size() != 2) {
            // 可能历时版本没有image特性
            binlogImage = BinlogImage.FULL;
        } else {
            binlogImage = BinlogImage.valuesOf(columnValues.get(1));
        }

        if (binlogFormat == null) {
            throw new IllegalStateException("unexpected binlog image query result:" + rs.getFieldValues());
        }
    }

    /**
     * 获取主库checksum信息
     * 
     * <pre>
     * mariadb区别于mysql会在binlog的第一个事件Rotate_Event里也会采用checksum逻辑,而mysql是在第二个binlog事件之后才感知是否需要处理checksum
     * 导致maraidb只要是开启checksum就会出现binlog文件名解析乱码
     * fixed issue : https://github.com/alibaba/canal/issues/1081
     * </pre>
     */
    private void loadBinlogChecksum() {
        ResultSetPacket rs = null;
        try {
            rs = query("select @@global.binlog_checksum");
            List<String> columnValues = rs.getFieldValues();
            if (columnValues != null && columnValues.size() >= 1 && columnValues.get(0) != null

View on GitHub (pinned to 87be50e876)