alibaba/canal · error · IOException

Read error:

Error message

Read  error: 

What it means

Thrown while parsing the status-variable block of a MySQL Query_log_event (e.g. BEGIN/COMMIT/DDL). The parser loops over status-var codes (Q_* constants) and this catch wraps any RuntimeException — buffer underflow, unknown code, or malformed length — into an IOException tagged with the offending status-var code name via findCodeName(code). The preceding default branch already logs-and-skips unknown codes, so this catch fires on structurally corrupt data: a truncated or misaligned status-var payload that breaks buffer reads.

Source

Thrown at dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/QueryLogEvent.java:913

                            }
                        }
                        break;
                    case Q_OPT_INDEX_FORMAT_PANDA_ENABLED:
                        // *start++ = thd->variables.opt_index_format_panda_enabled;
                        buffer.forward(1);
                        break;
                    default:
                        /*
                         * That's why you must write status vars in growing
                         * order of code
                         */
                        logger.error("Query_log_event has unknown status vars (first has code: " + code
                                     + "), skipping the rest of them");
                        return; // Break loop
                }
            }
        } catch (RuntimeException e) {
            throw new IOException("Read " + findCodeName(code) + " error: " + e.getMessage(), e);
        }
    }

    public final String getUser() {
        return user;
    }

    public final String getHost() {
        return host;
    }

    public final String getQuery() {
        return query;
    }

    public final String getCatalog() {
        return catalog;
    }

View on GitHub (pinned to 87be50e876)

Solutions

  1. Upgrade Canal to a release that supports the status-var codes your MySQL version emits (check the Q_* cases in QueryLogEvent around the throwing line).
  2. If the master is a fork, confirm the Canal build matches it (lizard/panda Q_LIZARD_* and Q_OPT_* branches must be present).
  3. Inspect the binlog with mysqlbinlog at the reported position to confirm the event is structurally valid; if corrupt, skip the position with canal.instance.master.position or reseed from a known-good position.
  4. If the code is genuinely unsupported, the existing default-branch logger.error already skips remaining vars — a RuntimeException before that means truncation, so verify binlog file integrity (checksum, file size vs index).

Example fix

// before: runtime exception during status-var read escapes as IOException
// after (defensive read, guard each code handler against remaining bytes):
int remaining = buffer.remaining();
if (code == Q_WHATEVER && remaining < needed) {
    logger.warn("Q_WHATEVER truncated (need {}, have {}), skipping", needed, remaining);
    return;
}
Defensive patterns

Strategy: try-catch

Try / catch

// At the CanalEventSink / parser boundary
try {
    eventSink.consume(event);
} catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Read ")) {
        // status-var parse failure — log position and skip the event
        logger.error("Status-var parse failure at {}:{}: {}",
            logContext.getLogFilename(), event.getHeader().getLogPos(), e.getMessage());
        // advance past this event to continue replication
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: A QueryLogEvent whose status-vars section is shorter than the declared length, contains a code whose handler reads more bytes than remain, or originates from a MySQL/MariaDB fork (lizard, panda, recycle-bin) emitting a code the reader only partially understands. The exception surfaces at parse time, before the event is handed to a Canal instance.

Common situations: Canal version older than the upstream MySQL that produced the binlog; replicating from a patched MySQL fork (AliSQL lizard/panda) without the matching Canal build; binlog corruption on disk or over a flaky network; a partial binlog read after an unclean master shutdown.

Related errors


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