alibaba/canal · critical · TableIdNotFoundException

not found tableId:

Error message

not found tableId:

What it means

Thrown by RowsLogEvent.fillTable when context.getTable(tableId) returns null. Every rows event (WRITE/UPDATE/DELETE) must be preceded by a TABLE_MAP event that registers the tableId with its column metadata in the LogContext. If no prior TABLE_MAP event was seen — or the map was cleared (clearAllTables runs on STMT_END_F) before this event — the lookup fails and TableIdNotFoundException aborts parsing.

Source

Thrown at dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/RowsLogEvent.java:203

            // mariadb compress log event
            // see https://github.com/alibaba/canal/issues/4388
            buffer = buffer.uncompressBuf();
            dataSize = buffer.limit();
            // rewrite type
            if (postHeaderLen == FormatDescriptionLogEvent.ROWS_HEADER_LEN_V2) {
                header.type = header.type - WRITE_ROWS_COMPRESSED_EVENT + WRITE_ROWS_EVENT;
            } else {
                header.type = header.type - WRITE_ROWS_COMPRESSED_EVENT_V1 + WRITE_ROWS_EVENT_V1;
            }
        }
        rowsBuf = buffer.duplicate(dataSize);
    }

    public final void fillTable(LogContext context) {
        table = context.getTable(tableId);

        if (table == null) {
            throw new TableIdNotFoundException("not found tableId:" + tableId);
        }

        // end of statement check:
        if ((flags & RowsLogEvent.STMT_END_F) != 0) {
            // Now is safe to clear ignored map (clear_tables will also
            // delete original table map events stored in the map).
            context.clearAllTables();
        }

        int jsonColumnCount = 0;
        int columnCnt = table.getColumnCnt();
        ColumnInfo[] columnInfo = table.getColumnInfo();
        for (int i = 0; i < columnCnt; i++) {
            ColumnInfo info = columnInfo[i];
            if (info.type == LogEvent.MYSQL_TYPE_JSON) {
                jsonColumnCount++;
            }
        }

View on GitHub (pinned to 87be50e876)

Solutions

  1. Start Canal at a transaction boundary (BEGIN/Format_description or a GTID event) so TABLE_MAP events precede rows events.
  2. Ensure table filtering does not drop TABLE_MAP events for tables whose rows you still receive — adjust canal.instance.filter.regex/black to be consistent.
  3. If the position is stale or corrupt, reset canal.instance.master.position (or the GTID) to a known-good start and clear the stored position.
  4. Upgrade Canal; older versions had bugs clearing the table map too eagerly on STMT_END_F.
Defensive patterns

Strategy: try-catch

Try / catch

try {
    rowsLogEvent.fillTable(logContext);
} catch (TableIdNotFoundException e) {
    logger.error("Table map missing for tableId={} at {}:{}; starting mid-transaction? Reposition at a BEGIN.",
        rowsLogEvent.getTableId(),
        logContext.getLogFilename(), rowsLogEvent.getHeader().getLogPos());
    // reposition the dump at the next transaction boundary or skip the event
}

Prevention

When it happens

Trigger: Canal starts mid-binlog at a position inside a transaction (after the TABLE_MAP but at a rows event); a TABLE_MAP event was filtered out or dropped; the LogContext was reset between the map event and the rows event; replicating a rows event whose tableId was never mapped because filtering excluded its table map.

Common situations: Starting Canal at a position that is not a transaction boundary; a binlog rotation or dump restart that loses buffered table-map state; filtering (canal.instance.filter) that skips the TABLE_MAP event but not its rows events; binlog corruption dropping a TABLE_MAP event.

Related errors


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