{"record":{"id":"8012e1d827a6ea0b","repo":"alibaba/canal","slug":"invalid-charset-id","errorCode":null,"errorMessage":"Invalid charset id: {}","messagePattern":"Invalid charset id: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/CharsetConversion.java","lineNumber":43,"sourceCode":"\r\n        Entry(final int id, String mysqlCharset, // NL\r\n              String mysqlCollation, String javaCharset){\r\n            this.charsetId = id;\r\n            this.mysqlCharset = mysqlCharset;\r\n            this.mysqlCollation = mysqlCollation;\r\n            this.javaCharset = javaCharset;\r\n            this.charset = Charset.isSupported(javaCharset) ? Charset.forName(javaCharset) : null;\r\n        }\r\n    }\r\n\r\n    // Character set data used in lookups. The array will be sparse.\r\n    static final Entry[] entries = new Entry[2048];\r\n\r\n    static Entry getEntry(final int id) {\r\n        if (id >= 0 && id < entries.length) {\r\n            return entries[id];\r\n        } else {\r\n            throw new IllegalArgumentException(\"Invalid charset id: \" + id);\r\n        }\r\n    }\r\n\r\n    // Loads character set information.\r\n    static void putEntry(final int charsetId, String mysqlCharset, String mysqlCollation, String javaCharset) {\r\n        entries[charsetId] = new Entry(charsetId, mysqlCharset, // NL\r\n            mysqlCollation,\r\n            javaCharset);\r\n    }\r\n\r\n    // Loads character set information.\r\n    @Deprecated\r\n    static void putEntry(final int charsetId, String mysqlCharset, String mysqlCollation) {\r\n        entries[charsetId] = new Entry(charsetId, mysqlCharset, // NL\r\n            mysqlCollation, /* Unknown java charset */\r\n            null);\r\n    }\r\n\r","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/CharsetConversion.java#L25-L61","documentation":"Thrown by CharsetConversion.getEntry(id) when a MySQL character-set id passed during binlog row decoding is negative or >= 2048 (the fixed size of the static 'entries' lookup array). The library maps numeric charset ids (carried in TABLE_MAP and row events) to Java Charset objects at load time via putEntry; ids in range but unregistered return null rather than throwing, so this fires only for genuinely out-of-range values.","triggerScenarios":"A binlog event supplies a column charset/collation numeric id that, when handed to CharsetConversion.getEntry(id), is < 0 or >= 2048. This happens while decoding column metadata in a TABLE_MAP_EVENT or writing row column values whose MysqlType carries a charset number.","commonSituations":"Corrupt or partially-written binlog where the 2-byte charset id was read as signed (e.g. a high bit set turning a valid id negative), a hand-crafted/malformed binlog stream from a non-standard source, or a future MySQL version emitting a collation id beyond the library's known set combined with a sign-extension bug in the read path.","solutions":["Confirm the id value in the message; if negative, suspect a signed-read bug in the upstream LogBuffer call feeding getEntry and verify the binlog is not truncated or corrupt.","Re-stream the binlog from a known-good position (earlier GTID/binlog file) to rule out transient corruption at the master.","If the id is legitimately valid but unregistered in this build, extend the putEntry table in CharsetConversion to register it and rebuild dbsync.","Validate the binlog with mysqlbinlog on the source server; if it errors there too, the binlog file itself is damaged and must be skipped/re-seeded."],"exampleFix":"// before\nEntry e = CharsetConversion.getEntry(charsetId); // throws for out-of-range id\n\n// after - guard before lookup\nif (charsetId < 0 || charsetId >= CharsetConversion.entries.length) {\n    logger.warn(\"Skipping column with out-of-range charset id: \" + charsetId);\n    return null;\n}\nEntry e = CharsetConversion.getEntry(charsetId);","handlingStrategy":"validation","validationCode":"// Validate charset id against the lookup array bounds before getEntry\npublic static Entry safeGetEntry(int id) {\n    if (id < 0 || id >= CharsetConversion.entries.length) {\n        return null; // caller decides how to treat unknown charset\n    }\n    return CharsetConversion.getEntry(id);\n}","typeGuard":null,"tryCatchPattern":"try { Entry e = CharsetConversion.getEntry(id); }\ncatch (IllegalArgumentException ex) {\n    // log + degrade: treat column as binary/unknown charset\n    logger.warn(\"Out-of-range charset id \" + id + \", treating as binary\", ex);\n}","preventionTips":["Treat getEntry returning null (in-range but unregistered) as the normal 'unknown charset' path, and reserve the thrown case for true range violations.","If you control the binlog source, keep server charset/collation ids within the registered table or extend putEntry.","Watch for signed-byte reads upstream that can flip a valid id negative."],"tags":["binlog","charset","decoding","illegalargument"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}