{"record":{"id":"12825e7ec00edfc0","repo":"alibaba/canal","slug":"reading-operation-type-invalid-operation-code","errorCode":null,"errorMessage":"reading operation type (invalid operation code)","messagePattern":"reading operation type \\(invalid operation code\\)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonDiffConversion.java","lineNumber":51,"sourceCode":"     * has the same effect as `JSON_REMOVE(col, path)`.\n     */\n    public static final int DIFF_OPERATION_REMOVE     = 2;\n\n    public static final int JSON_DIFF_OPERATION_COUNT = 3;\n\n    public static StringBuilder print_json_diff(LogBuffer buffer, long len, String columnName, int columnIndex,\n                                                String charsetName) {\n        return print_json_diff(buffer, len, columnName, columnIndex, Charset.forName(charsetName));\n    }\n\n    public static StringBuilder print_json_diff(LogBuffer buffer, long len, String columnName, int columnIndex,\n                                                Charset charset) {\n        int position = buffer.position();\n        List<String> operation_names = new ArrayList<>();\n        while (buffer.hasRemaining()) {\n            int operation_int = buffer.getUint8();\n            if (operation_int >= JSON_DIFF_OPERATION_COUNT) {\n                throw new IllegalArgumentException(\"reading operation type (invalid operation code)\");\n            }\n\n            // skip path\n            long path_length = buffer.getPackedLong();\n            if (path_length > len) {\n                throw new IllegalArgumentException(\"skipping path\");\n            }\n\n            // compute operation name\n            byte[] lastP = buffer.getData(buffer.position() + (int) path_length - 1, 1);\n            String operation_name = json_diff_operation_name(operation_int, lastP[0]);\n            operation_names.add(operation_name);\n\n            buffer.forward((int) path_length);\n            // skip value\n            if (operation_int != DIFF_OPERATION_REMOVE) {\n                long value_length = buffer.getPackedLong();\n                if (value_length > len) {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonDiffConversion.java#L33-L69","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nint operation_int = buffer.getUint8();\nif (operation_int >= JSON_DIFF_OPERATION_COUNT) {\n    throw new IllegalArgumentException(\"reading operation type (invalid operation code)\");\n}\n\n// after: log and skip unknown operations instead of crashing\nint operation_int = buffer.getUint8();\nif (operation_int >= JSON_DIFF_OPERATION_COUNT) {\n    logger.warn(\"Unknown JSON diff operation code {} at pos {}, skipping diff\", operation_int, buffer.position());\n    return builder;\n}","handlingStrategy":"validation","validationCode":"// The check already exists in the library; callers can pre-validate\n// by ensuring the column is a JSON partial-update diff before calling print_json_diff\nif (columnType != Types.JSON || !isPartialUpdateDiff(buffer)) {\n    // not a JSON diff, skip print_json_diff\n    return;\n}","typeGuard":null,"tryCatchPattern":"try {\n    StringBuilder sb = JsonDiffConversion.print_json_diff(buffer, len, columnName, columnIndex, charset);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"invalid operation code\")) {\n        logger.warn(\"Unknown JSON diff operation in column {} at pos={}, MySQL version may be unsupported\",\n            columnName, buffer.position());\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["json-diff","binlog","mysql","mysql-8","operation-code"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}