{"record":{"id":"2ae06a70ce7b1f16","repo":"vitessio/vitess","slug":"got-a-real-event-before-format-description-event","errorCode":null,"errorMessage":"got a real event before FORMAT_DESCRIPTION_EVENT: %#v","messagePattern":"got a real event before FORMAT_DESCRIPTION_EVENT: %#v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/binlog/binlog_streamer.go","lineNumber":337,"sourceCode":"\t\t// binlog settings change) that changes the format.\n\t\tif ev.IsFormatDescription() {\n\t\t\tformat, err = ev.Format()\n\t\t\tif err != nil {\n\t\t\t\treturn pos, fmt.Errorf(\"can't parse FORMAT_DESCRIPTION_EVENT: %v, event data: %#v\", err, ev)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// We can't parse anything until we get a FORMAT_DESCRIPTION_EVENT that\n\t\t// tells us the size of the event header.\n\t\tif format.IsZero() {\n\t\t\t// The only thing that should come before the FORMAT_DESCRIPTION_EVENT\n\t\t\t// is a fake ROTATE_EVENT, which the primary sends to tell us the name\n\t\t\t// of the current log file.\n\t\t\tif ev.IsRotate() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn pos, fmt.Errorf(\"got a real event before FORMAT_DESCRIPTION_EVENT: %#v\", ev)\n\t\t}\n\n\t\t// Strip the checksum, if any. We don't actually verify the checksum, so discard it.\n\t\tev, _, err = ev.StripChecksum(format)\n\t\tif err != nil {\n\t\t\treturn pos, fmt.Errorf(\"can't strip checksum from binlog event: %v, event data: %#v\", err, ev)\n\t\t}\n\n\t\tswitch {\n\t\tcase ev.IsPseudo():\n\t\t\tgtid, _, _, _, err = ev.GTID(format)\n\t\t\tif err != nil {\n\t\t\t\treturn pos, fmt.Errorf(\"can't get GTID from binlog event: %v, event data: %#v\", err, ev)\n\t\t\t}\n\t\t\toldpos := pos\n\t\t\tpos = replication.AppendGTID(pos, gtid)\n\t\t\t// If the event is received outside of a transaction, it must\n\t\t\t// be sent. Otherwise, it will get lost and the targets will go out","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/binlog/binlog_streamer.go#L319-L355","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify which MySQL flavor/version is streaming and that Vitess supports it","Check for proxy/MX layers altering the event order; stream directly from MySQL","Restart the binlog dump from a clean position so the format-description event is delivered first","If a custom server build produces this, capture the dumped event and compare against a real MySQL dump"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := streamer.Stream(ctx); err != nil {\n\tif strings.Contains(err.Error(), \"before FORMAT_DESCRIPTION_EVENT\") {\n\t\t// restart dump from a clean position; check for stream-intercepting middleware\n\t}\n}","preventionTips":["Stream directly from MySQL, not through proxies that alter the event stream","Ensure streams start from valid binlog positions","Use vanilla supported MySQL flavors"],"tags":["binlog","protocol","event-order"],"backgroundTag":"missing-format-description-event","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}