alibaba/canal · error · RuntimeException

Target column: {targetColumnName} not matched

Error message

Target column: {targetColumnName} not matched

What it means

Thrown during the INSERT sync path when a target column name declared in the RDB mapping config cannot be found in the target database table's metadata. The target column types are loaded once via getTargetColumnType(), which executes `SELECT * FROM targetTable WHERE 1=2` and caches column names (lowercased) under key `destination.database.table`. If the cleaned, lowercased target column name is absent from that metadata map, the sync aborts because Canal cannot determine the JDBC type needed for the prepared-statement parameter.

Source

Thrown at client-adapter/rdb/src/main/java/com/alibaba/otter/canal/client/adapter/rdb/service/RdbSyncService.java:287

        for (int i = 0; i < mapLen; i++) {
            insertSql.append("?,");
        }
        len = insertSql.length();
        insertSql.delete(len - 1, len).append(")");

        Map<String, Integer> ctype = getTargetColumnType(batchExecutor.getConn(), config);

        List<Map<String, ?>> values = new ArrayList<>();
        for (Map.Entry<String, String> entry : columnsMap.entrySet()) {
            String targetColumnName = entry.getKey();
            String srcColumnName = entry.getValue();
            if (srcColumnName == null) {
                srcColumnName = Util.cleanColumn(targetColumnName);
            }

            Integer type = ctype.get(Util.cleanColumn(targetColumnName).toLowerCase());
            if (type == null) {
                throw new RuntimeException("Target column: " + targetColumnName + " not matched");
            }
            Object value = data.get(srcColumnName);
            BatchExecutor.setValue(values, type, value);
        }

        try {
            batchExecutor.execute(insertSql.toString(), values);
        } catch (SQLException e) {
            if (skipDupException
                && (e.getMessage().contains("Duplicate entry") || e.getMessage().contains("duplicate key") || e.getMessage().startsWith("ORA-00001:"))) {
                // ignore
                // TODO 增加更多关系数据库的主键冲突的错误码
            } else {
                throw e;
            }
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Insert into target table, sql: {}", insertSql);

View on GitHub (pinned to 87be50e876)

Solutions

  1. Compare every key in the mapping config's targetColumns map against `DESC targetTable` or `SELECT * FROM targetTable WHERE 1=2` column names — fix any name that does not match.
  2. If the target schema changed after canal started, restart the canal adapter instance so columnsTypeCache is rebuilt from the current DDL.
  3. Verify the target database/schema in dbMapping.database and dbMapping.targetTable point to the correct target where the column actually exists.
  4. Ensure target column names in the config contain no stray backticks, quotes, or whitespace — cleanColumn removes backticks and single-quotes but not double-quotes or trailing spaces.

Example fix

// mapping config before (column 'user_name' does not exist in target table)
targetColumns:
  user_name: username

// after (corrected to match target table DDL)
targetColumns:
  username: username
Defensive patterns

Strategy: validation

Validate before calling

// Before sync, verify all target columns exist in the target table metadata
Connection conn = batchExecutor.getConn();
DbMapping dbMapping = config.getDbMapping();
DatabaseMetaData meta = conn.getMetaData();
try (ResultSet rs = meta.getColumns(null, null, dbMapping.getTargetTable(), null)) {
    Set<String> actualCols = new HashSet<>(String.CASE_INSENSITIVE_ORDER);
    while (rs.next()) {
        actualCols.add(rs.getString("COLUMN_NAME"));
    }
}
for (String targetCol : dbMapping.getTargetColumns().keySet()) {
    String cleaned = Util.cleanColumn(targetCol);
    if (!actualCols.contains(cleaned)) {
        throw new IllegalStateException(
            "Target column '" + targetCol + "' not found in table " + dbMapping.getTargetTable());
    }
}

Type guard

null

Try / catch

// Wrap the sync call and report schema mismatches clearly
try {
    rdbSyncService.sync(dmls);
} catch (RuntimeException e) {
    if (e.getMessage().contains("Target column:") && e.getMessage().contains("not matched")) {
        logger.error("Schema mismatch detected. Verify target table columns match mapping config: {}", e.getMessage());
        // trigger schema refresh by clearing cache and retrying once
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling RdbSyncService.sync() (insert path) where dbMapping.targetColumns lists a column that does not exist in the target table, or where the target table schema was altered (column renamed/dropped) after the column-type cache was populated, or where a column name has case/quote/backtick differences that cleanColumn+toLowerCase cannot reconcile.

Common situations: Mapping YAML references a column name with a typo or wrong casing; target DDL was migrated (column renamed) but the canal mapping config was not updated; a column uses special characters that cleanColumn strips inconsistently; target DB connection points to a different schema/database than expected.

Related errors


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