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
- 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.
- If the target schema changed after canal started, restart the canal adapter instance so columnsTypeCache is rebuilt from the current DDL.
- Verify the target database/schema in dbMapping.database and dbMapping.targetTable point to the correct target where the column actually exists.
- 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
- Run a config validation step at deployment that compares mapping config column names against the live target table DDL.
- Restart the canal adapter after any target schema migration to rebuild the column-type cache.
- Use a CI pipeline that validates mapping YAML column names against a schema definition before deploy.
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
- Target column: {} not matched
- Target column: {targetColumnName} not matched
- No tablestore adapter found for config key: {key}
- not allow to change outAdapterKey
- ERROR Config: {fileName} {errorMessage}
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/a048213923c1d7f7.
Report an issue: GitHub.