apache/seatunnel · error · IllegalStateException
Unknown operation type '${op}'.
Error message
Unknown operation type '${op}'. What it means
Canal JSON events carry an 'op' field (INSERT/UPDATE/DELETE/QUERY/CREATE/ALTER are recognized). When op holds any other value, the deserializer's switch falls through to default and throws IllegalStateException. The enclosing catch re-wraps it via CommonError.jsonOperationError unless ignoreParseErrors is enabled.
Source
Thrown at seatunnel-formats/seatunnel-format-json/src/main/java/org/apache/seatunnel/format/json/canal/CanalJsonDeserializationSchema.java:210
out.collect(before);
out.collect(after);
}
break;
case OP_DELETE:
for (int i = 0; i < dataNode.size(); i++) {
SeaTunnelRow row = convertJsonNode(dataNode.get(i));
row.setRowKind(RowKind.DELETE);
if (tablePath != null && !tablePath.toString().isEmpty()) {
row.setTableId(tablePath.toString());
}
if (tsNode != null) {
MetadataUtil.setEventTime(row, tsNode.asLong());
}
out.collect(row);
}
break;
default:
throw new IllegalStateException(
String.format("Unknown operation type '%s'.", op));
}
} catch (RuntimeException e) {
if (!ignoreParseErrors) {
throw CommonError.jsonOperationError(FORMAT, jsonNode.toString(), e);
}
}
}
private ObjectNode convertBytes(byte[] message) throws SeaTunnelRuntimeException {
if (message == null || message.length == 0) {
return null;
}
try {
return (ObjectNode) jsonDeserializer.deserializeToJsonNode(message);
} catch (Throwable t) {
if (!ignoreParseErrors) {View on GitHub (pinned to cf67b549a7)
Solutions
- Identify the unexpected op value in the message (logged in the jsonOperationError) and check your Canal server version for new op types
- Upgrade SeaTunnel's seatunnel-format-json to a version supporting the new Canal op types
- Filter or route unknown-op messages upstream before feeding the deserializer
- Set json.ignore-parse-errors=true to skip unparseable messages instead of failing the job
Example fix
// before (config)
json {
ignore-parse-errors = false
}
// after
json {
ignore-parse-errors = true
timestamp-format.standard = "SQL"
} Defensive patterns
Strategy: validation
Validate before calling
String op = node.path("op").asText("");
if (!op.matches("INSERT|UPDATE|DELETE|QUERY|CREATE|ALTER")) {
throw new IllegalStateException("Unrecognized Canal op: " + op);
} Type guard
boolean knownOp = Set.of("INSERT","UPDATE","DELETE","QUERY","CREATE","ALTER").contains(node.path("op").asText("")); Try / catch
try { canalJsonSchema.deserialize(message, out); } catch (SeaTunnelJsonFormatException e) { log.warn("Skipping message with unknown op"); /* or rethrow depending on ignore-parse-errors */ } Prevention
- Verify Canal server and SeaTunnel format versions are compatible for op codes
- Enable json.ignore-parse-errors=true to survive malformed foreign messages
- Isolate the Canal topic/queue from other producers writing arbitrary JSON
- Log full offending messages (jsonOperationError includes the payload) to diagnose op drift
When it happens
Trigger: Consuming a Canal JSON message whose op field contains an unrecognized value (new Canal op types, corrupted op field, non-Canal JSON injected into the stream, or truncated/garbled messages).
Common situations: Canal version introducing new op codes not yet supported by this SeaTunnel format; unrelated producers writing to the same topic/queue; message corruption or charset issues mangling the op field.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Null data value '${jsonNode}' Cannot send downstream
- COMMON-02
- Please invoke DeserializationSchema#deserialize(byte[], Coll
- Unknown operation type '${op}'.
- Unsupported type: %s
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/f267db5984598f6b.
Report an issue: GitHub.