apache/flink · error · IOException
Corrupt Canal JSON message '%s'.
Error message
Corrupt Canal JSON message '%s'.
What it means
Thrown by CanalJsonDeserializationSchema's catch-all when any Throwable escapes the per-message parsing pipeline (JSON syntax error, missing 'data'/'type' fields, unexpected shapes, field conversion failures). It wraps the original exception with the full raw message for diagnosis. With 'json.ignore-parse-errors' enabled it is logged at DEBUG and the message is skipped instead.
Source
Thrown at flink-formats/flink-json/src/main/java/org/apache/flink/formats/json/canal/CanalJsonDeserializationSchema.java:306
return;
} else {
if (!ignoreParseErrors) {
throw new IOException(
format(
"Unknown \"type\" value \"%s\". The Canal JSON message is '%s'",
type, new String(message)));
}
if (LOG.isDebugEnabled()) {
LOG.debug(
"Unknown \"type\" value '{}'. The Canal JSON message is '{}'.",
type,
new String(message));
}
}
} catch (Throwable t) {
// a big try catch to protect the processing.
if (!ignoreParseErrors) {
throw new IOException(
format("Corrupt Canal JSON message '%s'.", new String(message)), t);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Corrupt Canal JSON message '{}'.", new String(message), t);
}
}
for (GenericRowData genericRowData : genericRowDataList) {
out.collect(genericRowData);
}
}
private GenericRowData handleRow(GenericRowData rootRow, GenericRowData physicalRow) {
// shortcut in case no output projection is required
if (!hasMetadata) {
return physicalRow;
}
final int physicalArity = physicalRow.getArity();
final int metadataArity = metadataConverters.length;View on GitHub (pinned to 2f3c205e92)
Solutions
- Capture and pretty-print the failing raw message from the exception text and validate its Canal envelope (must have data/type, and old for updates)
- Fix the producer or topic routing so only well-formed Canal-JSON reaches this table
- Align the table schema with the 'data' payload's actual columns
- If occasional poison pills are acceptable, set 'json.ignore-parse-errors' = true (records are then skipped, not failed)
Example fix
// before
WITH ('connector'='kafka', 'format'='canal-json')
// after: tolerate poison pills (skips bad messages)
WITH ('connector'='kafka', 'format'='canal-json', 'json.ignore-parse-errors'='true') Defensive patterns
Strategy: try-catch
Validate before calling
JsonNode root;
try { root = mapper.readTree(message); } catch (IOException e) { /* poison pill: quarantine */ }
if (root.get("type") == null || root.get("data") == null) { /* not canal-json envelope */ } Try / catch
catch (IOException e) on 'Corrupt Canal JSON message' — log raw message + cause to a dead-letter store; enable ignore-parse-errors only with skip-rate monitoring.
Prevention
- Validate the envelope of a sample message before creating the table
- Isolate CDC topics from other producers
- Monitor skipped-message counters when ignore-parse-errors is on
When it happens
Trigger: Consuming non-JSON bytes, truncated messages, JSON lacking the Canal envelope ('data', 'type', 'old'), or payload values incompatible with the declared table schema, using format 'canal-json' with default options.
Common situations: Compacted/corrupted Kafka messages; misconfigured topic with mixed formats (plain JSON, Debezium, or log lines); partial writes from producers; schema drift between Canal-JSON payload and the Flink table columns.
Related errors
- Corrupt Debezium JSON message '%s'.
- Unknown "type" value "%s". The Canal JSON message is '%s'
- Could not serialize row '%s'.
- Please invoke DeserializationSchema#deserialize(byte[], Coll
- Unsupported operation '%s' for row kind.
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/2a6f6f9460498086.
Report an issue: GitHub.