alibaba/canal · error · IOException
Error binlog file header: {}
Error message
Error binlog file header: {} What it means
Thrown by FileLogFetcher.open() after reading exactly 4 header bytes that do not equal BINLOG_MAGIC ({0xfe, 'b','i','n'}). The file is long enough to have a header, but the magic bytes are wrong, so it is not a MySQL binlog file.
Source
Thrown at dbsync/src/main/java/com/taobao/tddl/dbsync/binlog/FileLogFetcher.java:84
*/
public void open(String filePath, final long filePosition) throws FileNotFoundException, IOException {
open(new File(filePath), filePosition);
}
/**
* Open binlog file in local disk to fetch.
*/
public void open(File file, final long filePosition) throws FileNotFoundException, IOException {
fin = new FileInputStream(file);
ensureCapacity(BIN_LOG_HEADER_SIZE);
if (BIN_LOG_HEADER_SIZE != fin.read(buffer, 0, BIN_LOG_HEADER_SIZE)) {
throw new IOException("No binlog file header");
}
if (buffer[0] != BINLOG_MAGIC[0] || buffer[1] != BINLOG_MAGIC[1] || buffer[2] != BINLOG_MAGIC[2]
|| buffer[3] != BINLOG_MAGIC[3]) {
throw new IOException("Error binlog file header: "
+ Arrays.toString(Arrays.copyOf(buffer, BIN_LOG_HEADER_SIZE)));
}
limit = 0;
origin = 0;
position = 0;
if (filePosition > BIN_LOG_HEADER_SIZE) {
final int maxFormatDescriptionEventLen = FormatDescriptionLogEvent.LOG_EVENT_MINIMAL_HEADER_LEN
+ FormatDescriptionLogEvent.ST_COMMON_HEADER_LEN_OFFSET
+ LogEvent.ENUM_END_EVENT + LogEvent.BINLOG_CHECKSUM_ALG_DESC_LEN
+ LogEvent.CHECKSUM_CRC32_SIGNATURE_LEN;
ensureCapacity(maxFormatDescriptionEventLen);
limit = fin.read(buffer, 0, maxFormatDescriptionEventLen);
limit = (int) getUint32(LogEvent.EVENT_LEN_OFFSET);
fin.getChannel().position(filePosition);
}
View on GitHub (pinned to 87be50e876)
Solutions
- Inspect the printed byte array in the message; {-2,98,105,110} would be correct magic, so any deviation confirms wrong content.
- Ensure the path is the raw mysqld binlog (typically mysql-bin.NNNNNN), not a .gz/.zip/.index/.sql sibling.
- Decompress first if the binlog was archived as gzip.
- Re-fetch the binlog from the master if the on-disk copy is the wrong file.
Example fix
// before
fetcher.open(new File(path), pos); // first 4 bytes != {0xfe,'b','i','n'}
// after - decompress archived binlogs before opening
File target = path.endsWith(".gz") ? gunzipTo(path) : new File(path);
fetcher.open(target, pos); Defensive patterns
Strategy: validation
Validate before calling
// Verify the binlog magic before handing the file to the fetcher
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
byte[] m = new byte[4];
raf.readFully(m);
if (m[0] != -2 || m[1] != 'b' || m[2] != 'i' || m[3] != 'n')
throw new IOException("not a binlog (bad magic): " + file);
} Try / catch
try { fetcher.open(file, pos); }
catch (IOException e) {
if (e.getMessage().startsWith("Error binlog file header"))
throw new IOException("wrong file type - decompress if gz / pick the raw mysql-bin.NNNNNN", e);
throw e;
} Prevention
- Open raw mysqld binlogs (mysql-bin.NNNNNN), not .gz/.index/.sql siblings.
- Decompress archived binlogs first.
- Cross-check magic bytes {0xfe,'b','i','n'} before opening.
When it happens
Trigger: FileLogFetcher reads 4 bytes into buffer and compares against BINLOG_MAGIC; the first four bytes differ (e.g. a gzip/relay-log.index/SQL text file), so it raises IOException with the offending bytes printed.
Common situations: Passing a compressed binlog (.gz), a relay-log index file, a plain SQL dump, a binary from a different engine, or a file with a byte-order mark prepended; reading the wrong file in a directory of similarly named files.
Related errors
- No binlog file header
- Unexcepted limit: {}
- Invalid ExecuteLoadQueryLogEvent: fn_pos_start=%d, fn_pos_en
- status_vars_len ( ) > data_len ( )
- clusterId and serverId are all empty
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/c455358a3219eaed.
Report an issue: GitHub.