{"record":{"id":"5b5c0ce432610de4","repo":"alibaba/canal","slug":"error-parser-of-eromanga-event-has-an-error-d-5b5c0c","errorCode":null,"errorMessage":"ERROR ## parser of eromanga-event has an error , data:${entry.toString()}","messagePattern":"ERROR ## parser of eromanga-event has an error , data:(.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"connector/core/src/main/java/com/alibaba/otter/canal/connector/core/util/MessageUtil.java","lineNumber":38,"sourceCode":"public class MessageUtil {\n\n    public static List<CommonMessage> convert(Message message) {\n        if (message == null) {\n            return null;\n        }\n        List<CanalEntry.Entry> entries = message.getEntries();\n        List<CommonMessage> msgs = new ArrayList<>(entries.size());\n        for (CanalEntry.Entry entry : entries) {\n            if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN\n                || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {\n                continue;\n            }\n\n            CanalEntry.RowChange rowChange;\n            try {\n                rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());\n            } catch (Exception e) {\n                throw new RuntimeException(\"ERROR ## parser of eromanga-event has an error , data:\" + entry.toString(),\n                    e);\n            }\n\n            CanalEntry.EventType eventType = rowChange.getEventType();\n\n            final CommonMessage msg = new CommonMessage();\n            msg.setIsDdl(rowChange.getIsDdl());\n            msg.setDatabase(entry.getHeader().getSchemaName());\n            msg.setTable(entry.getHeader().getTableName());\n            msg.setType(eventType.toString());\n            msg.setEs(entry.getHeader().getExecuteTime());\n            msg.setIsDdl(rowChange.getIsDdl());\n            msg.setTs(System.currentTimeMillis());\n            msg.setSql(rowChange.getSql());\n            msgs.add(msg);\n            List<Map<String, Object>> data = new ArrayList<>();\n            List<Map<String, Object>> old = new ArrayList<>();\n","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/connector/core/src/main/java/com/alibaba/otter/canal/connector/core/util/MessageUtil.java#L20-L56","documentation":"Thrown by MessageUtil.convert when CanalEntry.RowChange.parseFrom(entry.getStoreValue()) fails for a non-transaction entry. 'eromanga-event' is canal's internal codename for a row-data (ROWDATA) entry; the storeValue on such an entry must be a serialized RowChange. A parse failure means the entry's payload is missing, corrupted, or holds a structure that is not a RowChange.","triggerScenarios":"MessageUtil.convert(message) iterates entries; for any entry whose EntryType is not TRANSACTIONBEGIN/TRANSACTIONEND it calls RowChange.parseFrom(entry.getStoreValue()). Fails when storeValue is empty, contains a heartbeat/DDL marker that is not a RowChange, or is a newer-protobuf RowChange the client cannot parse.","commonSituations":"Mixing canal client/server versions where the RowChange proto schema differs; a heartbeat entry mistakenly typed as ROWDATA; memory/disk corruption of the storeValue; an entry produced by a custom plugin that wrote a non-RowChange payload into storeValue.","solutions":["Check entry.getEntryType() and entry.getHeader().getEventType() before calling convert — skip entries you do not expect to contain a RowChange.","Ensure canal connector and canal server versions are aligned so the RowChange protobuf schema matches.","If converting arbitrary Messages, wrap convert() per-message so one bad entry does not abort the whole batch, and inspect the offending entry.toString() shown in the message.","For heartbeat/DDL entries, handle them explicitly instead of letting RowChange.parseFrom run on a non-row payload."],"exampleFix":"// before\nList<CommonMessage> msgs = MessageUtil.convert(message);\n\n// after — filter to ROWDATA entries and isolate parse failures\nfor (CanalEntry.Entry entry : message.getEntries()) {\n    if (entry.getEntryType() != CanalEntry.EntryType.ROWDATA) continue;\n    try {\n        CanalEntry.RowChange rc = CanalEntry.RowChange.parseFrom(entry.getStoreValue());\n        // ... build CommonMessage\n    } catch (Exception e) {\n        log.warn(\"skip unparseable entry {}\", entry.getHeader(), e);\n    }\n}","handlingStrategy":"try-catch","validationCode":"// Skip entries that cannot contain a RowChange before convert()\nList<CanalEntry.Entry> rowdata = message.getEntries().stream()\n    .filter(e -> e.getEntryType() == CanalEntry.EntryType.ROWDATA)\n    .collect(Collectors.toList());","typeGuard":"static boolean isRowDataEntry(CanalEntry.Entry e) {\n    return e.getEntryType() == CanalEntry.EntryType.ROWDATA\n        && e.getStoreValue() != null\n        && !e.getStoreValue().isEmpty();\n}","tryCatchPattern":"for (CanalEntry.Entry entry : message.getEntries()) {\n    if (entry.getEntryType() != CanalEntry.EntryType.ROWDATA) continue;\n    try {\n        CanalEntry.RowChange rc = CanalEntry.RowChange.parseFrom(entry.getStoreValue());\n        // build CommonMessage...\n    } catch (Exception e) {\n        log.warn(\"skipping unparseable entry {}\", entry.getHeader(), e);\n    }\n}","preventionTips":["Keep canal client and server protobuf versions identical.","Filter to ROWDATA entries before parsing RowChange.","Isolate per-entry parsing so one bad entry does not abort the whole convert()."],"tags":["protobuf","canal-connector","data-corruption","version-mismatch"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}