{"record":{"id":"b8c6e276df299aa7","repo":"alibaba/canal","slug":"status-vars-len-data-len","errorCode":null,"errorMessage":"status_vars_len ( ) > data_len ( )","messagePattern":"status_vars_len \\( \\) > data_len \\( \\)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/QueryLogEvent.java","lineNumber":440,"sourceCode":"        final int dbLen = buffer.getUint8(); // Q_DB_LEN_OFFSET\n        errorCode = buffer.getUint16(); // Q_ERR_CODE_OFFSET\n\n        /*\n         * 5.0 format starts here. Depending on the format, we may or not have\n         * affected/warnings etc The remaining post-header to be parsed has\n         * length:\n         */\n        int statusVarsLen = 0;\n        if (postHeaderLen > QUERY_HEADER_MINIMAL_LEN) {\n            statusVarsLen = buffer.getUint16(); // Q_STATUS_VARS_LEN_OFFSET\n            /*\n             * Check if status variable length is corrupt and will lead to very\n             * wrong data. We could be even more strict and require data_len to\n             * be even bigger, but this will suffice to catch most corruption\n             * errors that can lead to a crash.\n             */\n            if (statusVarsLen > Math.min(dataLen, MAX_SIZE_LOG_EVENT_STATUS)) {\n                throw new IOException(\"status_vars_len (\" + statusVarsLen + \") > data_len (\" + dataLen + \")\");\n            }\n            dataLen -= statusVarsLen;\n        }\n        /*\n         * We have parsed everything we know in the post header for QUERY_EVENT,\n         * the rest of post header is either comes from older version MySQL or\n         * dedicated to derived events (e.g. Execute_load_query...)\n         */\n\n        /* variable-part: the status vars; only in MySQL 5.0 */\n        final int start = commonHeaderLen + postHeaderLen;\n        final int limit = buffer.limit(); /* for restore */\n        final int end = start + statusVarsLen;\n        buffer.position(start).limit(end);\n        unpackVariables(buffer, end);\n        buffer.position(end);\n        buffer.limit(limit);\n        /* A 2nd variable part; this is common to all versions */","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/event/QueryLogEvent.java#L422-L458","documentation":"Thrown during QueryLogEvent construction when the status variables length (statusVarsLen, read from the post-header in MySQL 5.0+ format) exceeds the minimum of the data length and MAX_SIZE_LOG_EVENT_STATUS. This catches corrupt status_vars_len values that would cause the parser to read far beyond the event boundary and potentially crash.","triggerScenarios":"When postHeaderLen > QUERY_HEADER_MINIMAL_LEN (meaning MySQL 5.0+ format with the Q_STATUS_VARS_LEN_OFFSET field), the code reads statusVarsLen as a uint16. If statusVarsLen > Math.min(dataLen, MAX_SIZE_LOG_EVENT_STATUS), the value is deemed corrupt.","commonSituations":"Corrupt QueryLogEvent where the status_vars_len field has a garbage value, a MySQL version that writes more status variables than the parser's MAX_SIZE_LOG_EVENT_STATUS cap allows, or buffer desynchronization causing the wrong bytes to be read as statusVarsLen.","solutions":["Log statusVarsLen, dataLen, and MAX_SIZE_LOG_EVENT_STATUS to identify the violated constraint.","Verify the FormatDescriptionLogEvent's postHeaderLen for QUERY_EVENT is correct for the MySQL version.","Check if a newer MySQL version writes larger status variable blocks that exceed MAX_SIZE_LOG_EVENT_STATUS — upgrade the parser if so.","Hex-dump the post-header region to verify the statusVarsLen byte position."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-validate: check if the format version includes status vars\nint postHeaderLen = descriptionEvent.postHeaderLen[header.type - 1];\nif (postHeaderLen > QueryLogEvent.QUERY_HEADER_MINIMAL_LEN) {\n    // status vars are present — verify data length is sufficient\n    int dataLen = buffer.limit() - (descriptionEvent.commonHeaderLen + postHeaderLen);\n    if (dataLen < 0) {\n        logger.warn(\"Negative data length for QueryLogEvent with status vars\");\n        return;\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    QueryLogEvent event = new QueryLogEvent(header, buffer, descriptionEvent, compatiablePercona, compress);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"status_vars_len\")) {\n        logger.warn(\"Corrupt status_vars_len in QueryLogEvent — possible MySQL version or format mismatch\");\n    }\n    throw e;\n}","preventionTips":["Verify the FormatDescriptionLogEvent postHeaderLen for QUERY_EVENT matches the MySQL server version.","Upgrade canal/dbsync when upgrading MySQL — newer versions may write larger status variable blocks.","Hex-dump the post-header region to verify the status_vars_len field position."],"tags":["binlog","mysql","query-event","status-vars","buffer-bounds","validation"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}