vitessio/vitess · critical

can't parse binlog event: invalid data: %#v

Error message

can't parse binlog event: invalid data: %#v

What it means

parseEvent rejects any binlog event that fails ev.IsValid() — the raw event bytes from the MySQL binlog connection are malformed/truncated and cannot be decoded. This is a hard data-integrity failure of the binlog stream.

Source

Thrown at go/vt/vttablet/tabletserver/vstreamer/vstreamer.go:523

			if err := injectHeartbeat(!ok, checkResult.Summary()); err != nil {
				if err == io.EOF {
					return nil
				}
				vs.vse.errorCounts.Add("Send", 1)
				return vterrors.Wrapf(err, "failed to send heartbeat event")
			}
		}
	}
}

// parseEvent parses an event from the binlog and converts it to a list of VEvents.
// The bufferAndTransmit function must be passed if the event is a TransactionPayloadEvent
// as for larger payloads (> ZstdInMemoryDecompressorMaxSize) the internal events need
// to be streamed directly here in order to avoid holding the entire payload's contents,
// which can be 10s or even 100s of GiBs, all in memory.
func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent, bufferAndTransmit func(vevent *binlogdatapb.VEvent) error) ([]*binlogdatapb.VEvent, error) {
	if !ev.IsValid() {
		return nil, fmt.Errorf("can't parse binlog event: invalid data: %#v", ev)
	}

	// We need to keep checking for FORMAT_DESCRIPTION_EVENT even after we've
	// seen one, because another one might come along (e.g. on log rotate due to
	// binlog settings change) that changes the format.
	if ev.IsFormatDescription() {
		var err error
		vs.format, err = ev.Format()
		if err != nil {
			return nil, fmt.Errorf("can't parse FORMAT_DESCRIPTION_EVENT: %v, event data: %#v", err, ev)
		}
		vs.eventGTID = nil
		return nil, nil
	}

	// We can't parse anything until we get a FORMAT_DESCRIPTION_EVENT that
	// tells us the size of the event header.
	if vs.format.IsZero() {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Check mysqld logs and run CHECK TABLE / verify binlog integrity on the source; corruption is usually on the MySQL side.
  2. Restart the vstreamer from an earlier GTID or from the start of the current binlog file; if the file is corrupt, purge/rotate binlogs and resync the workflow.
  3. Compare MySQL server version with vitess support matrix; if using an exotic binlog format/feature (e.g. unusual encryption), disable it or upgrade vitess.
  4. If reproducible on healthy binlogs, capture the event dump (%#v output) and file a vitess bug with the MySQL version.
Defensive patterns

Strategy: retry

Try / catch

err := vs.streamLog(ctx)
if err != nil && strings.Contains(err.Error(), "can't parse binlog event") {
    // binlog corruption: restart from an earlier GTID / rotate logs, then resync
    restartFromEarlierGTID(ctx)
    if stillFailing { resyncWorkflow(ctx) }
}

Prevention

When it happens

Trigger: mysql.BinlogEvent delivered by the binlog connection where IsValid() returns false — truncated or corrupt event buffer, protocol desync, or garbage bytes from the binlog dump.

Common situations: Corrupted binlog files on disk (disk issues, abnormal mysqld kill), network truncation between mysqld and the tablet, MySQL/binlog format incompatibilities, binlog rotated mid-read with corruption.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/d354bf9fe8dc8910. Report an issue: GitHub.