alibaba/canal · error · IllegalArgumentException

!! Don't know how to handle column type=%d meta=%d (%04X)

Error message

!! Don't know how to handle column type=%d meta=%d (%04X)

What it means

Thrown by RowsLogBuffer.fetchValue when decoding a column declared MYSQL_TYPE_STRING whose metadata (meta) is >= 256. The code splits meta into byte0 (high) and byte1 (low); if byte0 has the 0x30 bits set (indicating a real STRING/ENUM/SET) but byte0 itself is none of MYSQL_TYPE_SET, MYSQL_TYPE_ENUM, or MYSQL_TYPE_STRING, the encoded type is unrecognized. This is a defensive guard for a STRING-type column whose real-type byte doesn't map to any known MySQL string family, as described in MySQL bug #37426.

Source

Thrown at dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/RowsLogBuffer.java:298

        if (type == LogEvent.MYSQL_TYPE_STRING) {
            if (meta >= 256) {
                int byte0 = meta >> 8;
                int byte1 = meta & 0xff;
                if ((byte0 & 0x30) != 0x30) {
                    /* a long CHAR() field: see #37426 */
                    len = byte1 | (((byte0 & 0x30) ^ 0x30) << 4);
                    type = byte0 | 0x30;
                } else {
                    switch (byte0) {
                        case LogEvent.MYSQL_TYPE_SET:
                        case LogEvent.MYSQL_TYPE_ENUM:
                        case LogEvent.MYSQL_TYPE_STRING:
                            type = byte0;
                            len = byte1;
                            break;
                        default:
                            throw new IllegalArgumentException(String
                                .format("!! Don't know how to handle column type=%d meta=%d (%04X)", type, meta, meta));
                    }
                }
            } else {
                len = meta;
            }
        }

        switch (type) {
            case LogEvent.MYSQL_TYPE_LONG: {
                // XXX: How to check signed / unsigned?
                // value = unsigned ? Long.valueOf(buffer.getUint32()) :
                // Integer.valueOf(buffer.getInt32());
                value = valueOf(buffer.getInt32());
                javaType = Types.INTEGER;
                length = 4;
                break;
            }

View on GitHub (pinned to 87be50e876)

Solutions

  1. Upgrade Canal to match the master's MySQL version so all real-type bytes are recognized.
  2. Verify the column DDL on the master matches what Canal's table-map cache expects (re-snapshot if schema drifted).
  3. If the column is genuinely new, add its type constant to the switch in RowsLogBuffer.fetchValue and map it to a sensible java.sql.Types.
  4. Filter the offending table out of replication (canal.instance.filter.regex) until Canal supports it.

Example fix

// before
case LogEvent.MYSQL_TYPE_SET:
case LogEvent.MYSQL_TYPE_ENUM:
case LogEvent.MYSQL_TYPE_STRING:
    type = byte0;
    len = byte1;
    break;
default:
    throw new IllegalArgumentException(String.format("!! Don't know how to handle column type=%d meta=%d (%04X)", type, meta, meta));
// after: treat unknown real-type byte as plain STRING instead of aborting
default:
    logger.warn("Unknown STRING real-type byte {} (meta={}), treating as STRING", byte0, meta);
    type = LogEvent.MYSQL_TYPE_STRING;
    len = byte1;
Defensive patterns

Strategy: validation

Validate before calling

// Before relying on fetchValue, validate the column type/meta against a known-good schema
boolean isKnownStringSubtype(int byte0) {
    return byte0 == LogEvent.MYSQL_TYPE_SET
        || byte0 == LogEvent.MYSQL_TYPE_ENUM
        || byte0 == LogEvent.MYSQL_TYPE_STRING;
}

Try / catch

try {
    buffer.fetchValue(name, index, type, meta, isBinary);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("!! Don't know how to handle column type")) {
        logger.error("Unsupported STRING column type={}/meta={} in table {}, skipping event", type, meta, tableId);
        // optionally skip or null the column value
    } else throw e;
}

Prevention

When it happens

Trigger: A Table_map_event advertises a column of type MYSQL_TYPE_STRING with a meta word whose high byte carries a type the switch doesn't enumerate. Produced by MySQL forks adding new string-like subtypes, or by binlog corruption altering the meta field.

Common situations: Replicating from a MySQL/MariaDB version newer than what Canal was built against; a corrupted Table_map event; a custom column type plugin on the master.

Related errors


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