vitessio/vitess · error
unexpected event on table %s
Error message
unexpected event on table %s
What it means
vplayer received a RowEvent for a table that has no entry in vp.tablePlans — the plan map built from the workflow's filter rules when the stream started. Since there is no table plan, the row change cannot be transformed/applied to the target, so the event is rejected as unexpected.
Source
Thrown at go/vt/vttablet/tabletmanager/vreplication/vplayer.go:411
"vreplication: malformed row change with no row images in event for table %s", tableName)
case change.GetBefore() != nil && change.GetAfter() == nil:
insertsOnly = false
case change.GetBefore() == nil && change.GetAfter() != nil:
deletesOnly = false
default: // Update-shaped (both images).
deletesOnly, insertsOnly = false, false
}
}
return deletesOnly, insertsOnly, nil
}
func (vp *vplayer) applyRowEvent(ctx context.Context, rowEvent *binlogdatapb.RowEvent) error {
if err := vp.updateFKCheck(ctx, rowEvent.Flags); err != nil {
return err
}
tplan := vp.tablePlans[rowEvent.TableName]
if tplan == nil {
return fmt.Errorf("unexpected event on table %s", rowEvent.TableName)
}
applyFunc := func(sql string) (*sqltypes.Result, error) {
start := time.Now()
qr, err := vp.query(ctx, sql)
vp.vr.stats.QueryCount.Add(vp.phase, 1)
vp.vr.stats.QueryTimings.Record(vp.phase, start)
return qr, err
}
if vp.batchMode && len(rowEvent.RowChanges) > 1 {
deletesOnly, insertsOnly, err := bulkApplicableShapes(rowEvent.TableName, rowEvent.RowChanges)
if err != nil {
return err
}
// If we have multiple delete row events for a table with a single PK column
// then we can perform a simple bulk DELETE using an IN clause.
if deletesOnly && tplan.MultiDelete != nil {
_, err := tplan.applyBulkDeleteChanges(rowEvent.RowChanges, applyFunc, vp.vr.dbClient.maxBatchSize)View on GitHub (pinned to 01a25a7d17)
Solutions
- Confirm the workflow filter rules cover exactly the tables being streamed, and that the event table should really be included
- If the table is new on the source, restart/re-create the workflow so tablePlans are rebuilt including it
- Check for table name case or keyspace mismatches between source and filter rules
- If the event should be excluded, tighten the filter rules (no overly broad patterns) and restart the stream
Defensive patterns
Strategy: validation
Validate before calling
// Before starting/restarting the workflow, confirm every streamed table matches a filter rule
for _, tbl := range sourceTables {
if !matchesAnyRule(workflowFilter, tbl) && !explicitlyExcluded(tbl) {
// tighten or broaden rules so tablePlans cover exactly the intended set
}
} Try / catch
if err := applyEvent(ctx, ev); err != nil && strings.Contains(err.Error(), "unexpected event on table") {
// restart workflow to rebuild tablePlans including/excluding that table
} Prevention
- Restart the workflow after changing filter rules or after adding tables on the source
- Avoid overly broad filter patterns (e.g. /.*) that accidentally catch new tables
- Watch for table-name case mismatches between source and filter rules
- Apply source DDL deliberately during active workflows and review filter impact
When it happens
Trigger: A RowEvent arrives (after updateFKCheck succeeds) whose rowEvent.TableName is absent from vp.tablePlans — typically because the table was created/altered on the source after the stream began, or the filter/keyspace/table mismatch means the plan was never built for it.
Common situations: DDL on the source adding a new table mid-workflow that the filter unintentionally matches (e.g. broad /.* rule); keyspace or table name case-mismatch; source shard reconfigured so events from an unfiltered table leak in; stale stream state after filter changes without restarting the workflow.
Related errors
- unsupported mix of '*' and columns
- unsupported qualifier for '*' expression
- unsupported non-select statement
- unsupported distinct clause
- unsupported select from dual
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/2d3632aeb560c1c7.
Report an issue: GitHub.