vitessio/vitess · error

got a real event before FORMAT_DESCRIPTION_EVENT: %#v

Error message

got a real event before FORMAT_DESCRIPTION_EVENT: %#v

What it means

The binlog protocol guarantees that the first meaningful event in a dump is the FORMAT_DESCRIPTION_EVENT (only a fake ROTATE_EVENT may precede it). If parseEvents sees any other real event before it has a format, it cannot safely decode anything and aborts with a dump of the offending event.

Source

Thrown at go/vt/binlog/binlog_streamer.go:337

		// binlog settings change) that changes the format.
		if ev.IsFormatDescription() {
			format, err = ev.Format()
			if err != nil {
				return pos, fmt.Errorf("can't parse FORMAT_DESCRIPTION_EVENT: %v, event data: %#v", err, ev)
			}
			continue
		}

		// We can't parse anything until we get a FORMAT_DESCRIPTION_EVENT that
		// tells us the size of the event header.
		if format.IsZero() {
			// The only thing that should come before the FORMAT_DESCRIPTION_EVENT
			// is a fake ROTATE_EVENT, which the primary sends to tell us the name
			// of the current log file.
			if ev.IsRotate() {
				continue
			}
			return pos, fmt.Errorf("got a real event before FORMAT_DESCRIPTION_EVENT: %#v", ev)
		}

		// Strip the checksum, if any. We don't actually verify the checksum, so discard it.
		ev, _, err = ev.StripChecksum(format)
		if err != nil {
			return pos, fmt.Errorf("can't strip checksum from binlog event: %v, event data: %#v", err, ev)
		}

		switch {
		case ev.IsPseudo():
			gtid, _, _, _, err = ev.GTID(format)
			if err != nil {
				return pos, fmt.Errorf("can't get GTID from binlog event: %v, event data: %#v", err, ev)
			}
			oldpos := pos
			pos = replication.AppendGTID(pos, gtid)
			// If the event is received outside of a transaction, it must
			// be sent. Otherwise, it will get lost and the targets will go out

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Verify which MySQL flavor/version is streaming and that Vitess supports it
  2. Check for proxy/MX layers altering the event order; stream directly from MySQL
  3. Restart the binlog dump from a clean position so the format-description event is delivered first
  4. If a custom server build produces this, capture the dumped event and compare against a real MySQL dump
Defensive patterns

Strategy: try-catch

Try / catch

if err := streamer.Stream(ctx); err != nil {
	if strings.Contains(err.Error(), "before FORMAT_DESCRIPTION_EVENT") {
		// restart dump from a clean position; check for stream-intercepting middleware
	}
}

Prevention

When it happens

Trigger: During Stream/parseEvents, before any FORMAT_DESCRIPTION_EVENT is processed, an event arrives that is neither a rotate event nor a format description — e.g. the server sent a PREVIOUS_GTIDS/query event immediately, or events got reordered/corrupted.

Common situations: Pointing the streamer at a non-standard binlog stream; corruption or interception in the dump stream; unusual server flavors that send different leading events; mid-file restart delivering events out of order.

Related errors


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