alibaba/canal · error · IllegalArgumentException
reading operation type (invalid operation code)
Error message
reading operation type (invalid operation code)
What it means
Thrown during the first pass of JSON partial-update diff parsing (MySQL 8.0 JSON partial updates in binlog). The operation code byte read via getUint8() is >= JSON_DIFF_OPERATION_COUNT (3), meaning it does not correspond to any known diff operation: 0=REPLACE, 1=INSERT, 2=REMOVE. This indicates the diff payload is corrupt, misaligned, or produced by a MySQL version with additional operation types.
Source
Thrown at dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonDiffConversion.java:51
* has the same effect as `JSON_REMOVE(col, path)`.
*/
public static final int DIFF_OPERATION_REMOVE = 2;
public static final int JSON_DIFF_OPERATION_COUNT = 3;
public static StringBuilder print_json_diff(LogBuffer buffer, long len, String columnName, int columnIndex,
String charsetName) {
return print_json_diff(buffer, len, columnName, columnIndex, Charset.forName(charsetName));
}
public static StringBuilder print_json_diff(LogBuffer buffer, long len, String columnName, int columnIndex,
Charset charset) {
int position = buffer.position();
List<String> operation_names = new ArrayList<>();
while (buffer.hasRemaining()) {
int operation_int = buffer.getUint8();
if (operation_int >= JSON_DIFF_OPERATION_COUNT) {
throw new IllegalArgumentException("reading operation type (invalid operation code)");
}
// skip path
long path_length = buffer.getPackedLong();
if (path_length > len) {
throw new IllegalArgumentException("skipping path");
}
// compute operation name
byte[] lastP = buffer.getData(buffer.position() + (int) path_length - 1, 1);
String operation_name = json_diff_operation_name(operation_int, lastP[0]);
operation_names.add(operation_name);
buffer.forward((int) path_length);
// skip value
if (operation_int != DIFF_OPERATION_REMOVE) {
long value_length = buffer.getPackedLong();
if (value_length > len) {View on GitHub (pinned to 87be50e876)
Solutions
- Verify the column is actually a JSON partial-update diff and not a regular JSON value or other type.
- Check MySQL server version for new JSON diff operation types; upgrade canal/dbsync if a newer MySQL added operations beyond REPLACE/INSERT/REMOVE.
- Hex-dump the diff payload to confirm the operation byte and detect misalignment.
- Upgrade to a canal version that matches the MySQL 8.0 minor version generating the binlog.
Example fix
// before
int operation_int = buffer.getUint8();
if (operation_int >= JSON_DIFF_OPERATION_COUNT) {
throw new IllegalArgumentException("reading operation type (invalid operation code)");
}
// after: log and skip unknown operations instead of crashing
int operation_int = buffer.getUint8();
if (operation_int >= JSON_DIFF_OPERATION_COUNT) {
logger.warn("Unknown JSON diff operation code {} at pos {}, skipping diff", operation_int, buffer.position());
return builder;
} Defensive patterns
Strategy: validation
Validate before calling
// The check already exists in the library; callers can pre-validate
// by ensuring the column is a JSON partial-update diff before calling print_json_diff
if (columnType != Types.JSON || !isPartialUpdateDiff(buffer)) {
// not a JSON diff, skip print_json_diff
return;
} Try / catch
try {
StringBuilder sb = JsonDiffConversion.print_json_diff(buffer, len, columnName, columnIndex, charset);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("invalid operation code")) {
logger.warn("Unknown JSON diff operation in column {} at pos={}, MySQL version may be unsupported",
columnName, buffer.position());
}
throw e;
} Prevention
- Verify the MySQL 8.0 server version is supported by the canal/dbsync release.
- Only call print_json_diff for confirmed JSON partial-update diff columns.
- Upgrade canal when upgrading MySQL 8.0 minor versions.
When it happens
Trigger: Calling JsonDiffConversion.print_json_diff() on a buffer positioned at a JSON diff column value from a MySQL 8.0 partial JSON update row event. The first byte of each operation entry is the operation code; if it reads >= 3, the exception fires.
Common situations: MySQL 8.0+ introducing new JSON partial-update operation types not yet supported by the parser, buffer position misalignment from a prior column parse error, binlog corruption, or processing a non-diff value as if it were a diff (column type mapping error).
Related errors
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/12825e7ec00edfc0.
Report an issue: GitHub.