alibaba/canal · error · IllegalArgumentException

parsing json value

Error message

parsing json value

What it means

Thrown during the second pass (print pass) of JSON diff parsing when the JSON value parsed from the diff payload has type Json_enum_type.ERROR. After calling JsonConversion.parse_value() on the value bytes within a REPLACE or INSERT operation, the result is checked for the ERROR sentinel type, indicating the value could not be parsed.

Source

Thrown at dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/JsonDiffConversion.java:134

            // Read path length
            long path_length = buffer.getPackedLong();
            // Print path
            builder.append('\'').append(buffer.getFixString((int) path_length)).append('\'');

            if (operation_int != DIFF_OPERATION_REMOVE) {
                // Print comma between path and value
                builder.append(", ");
                // Read value length
                long value_length = buffer.getPackedLong();

                Json_Value jsonValue = JsonConversion.parse_value(buffer.getUint8(),
                    buffer,
                    value_length - 1,
                    charset);
                buffer.forward((int) value_length - 1);
                // Read value
                if (jsonValue.m_type == Json_enum_type.ERROR) {
                    throw new IllegalArgumentException("parsing json value");
                }
                StringBuilder jsonBuilder = new StringBuilder();
                jsonValue.toJsonString(jsonBuilder, charset);
                builder.append(jsonBuilder);
            }

            // see https://github.com/alibaba/canal/pull/5018
            if (buffer.position() - position >= len) {
                builder.append(")");
                break;
            }

            // Print closing parenthesis
            if (!buffer.hasRemaining() || !Objects.equals(operation_names.get(diff_i + 1), operation_names.get(diff_i))) {
                builder.append(")");
            }

            if (buffer.hasRemaining()) {

View on GitHub (pinned to 87be50e876)

Solutions

  1. Check that value_length is > 1 before subtracting 1 to avoid underflow when cast to int.
  2. Verify the JSONB type byte within the value portion is a recognized constant (0x0-0xC, 0xF).
  3. Upgrade canal/dbsync to support the MySQL version producing the partial updates.
  4. Hex-dump the value bytes within the diff entry to inspect the type byte.

Example fix

// before
long value_length = buffer.getPackedLong();
Json_Value jsonValue = JsonConversion.parse_value(buffer.getUint8(), buffer, value_length - 1, charset);

// after: guard against underflow and ERROR
long value_length = buffer.getPackedLong();
if (value_length < 1) {
    logger.warn("Invalid value_length {} in JSON diff, skipping", value_length);
    return builder;
}
int valueType = buffer.getUint8();
Json_Value jsonValue = JsonConversion.parse_value(valueType, buffer, value_length - 1, charset);
if (jsonValue.m_type == Json_enum_type.ERROR) {
    logger.warn("Failed to parse JSON diff value with type 0x{}", Integer.toHexString(valueType));
    return builder;
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-validate value_length before parse_value is called by the library
// (this check must be done inside a fork or by wrapping the call)
// At minimum, ensure value_length > 1 to prevent underflow in value_length - 1
if (value_length < 1) {
    logger.warn("JSON diff value_length underflow: {}", value_length);
    return;
}

Try / catch

try {
    JsonDiffConversion.print_json_diff(buffer, len, columnName, columnIndex, charset);
} catch (IllegalArgumentException e) {
    if (e.getMessage().equals("parsing json value")) {
        logger.warn("Embedded JSON value in diff could not be parsed, column={}", columnName);
    }
    throw e;
}

Prevention

When it happens

Trigger: In print_json_diff's second-pass while loop, for non-REMOVE operations, the code reads value_length, then calls JsonConversion.parse_value with the value's type byte. If parse_value returns a Json_Value with m_type == ERROR (which can happen if the internal parsing produced a sentinel rather than throwing), this exception fires.

Common situations: An embedded JSON value within the diff uses an unsupported type byte, the value_length - 1 parameter underflows (value_length is 0, making it -1 when cast to int), or the value data is corrupt/truncated. MySQL 8.0+ introducing new JSONB types not handled by parse_value.

Related errors


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/06886e829bc9b65c. Report an issue: GitHub.