vitessio/vitess · error
cannot update progress with a nil diff report
Error message
cannot update progress with a nil diff report
What it means
tableDiffer.updateTableProgress is invoked with dr (*DiffReport) = nil while trying to persist per-table vdiff progress. Progress updates require a valid diff report to marshal to JSON and write to the _vt.vreplication message/progress state, so a nil report is rejected explicitly instead of panicking later in json.Marshal.
Source
Thrown at go/vt/vttablet/tabletmanager/vdiff/table_differ.go:791
// If the collation is nil or unknown, use binary collation to compare as bytes.
collationID = col.collation
if collationID == collations.Unknown {
collationID = collations.CollationBinaryID
}
c, err = evalengine.NullsafeCompare(sourceRow[compareIndex], targetRow[compareIndex], td.wd.collationEnv, collationID, nil)
if err != nil {
return 0, err
}
if c != 0 {
return c, nil
}
}
return 0, nil
}
func (td *tableDiffer) updateTableProgress(dbClient binlogplayer.DBClient, dr *DiffReport, lastRow []sqltypes.Value) error {
if dr == nil {
return errors.New("cannot update progress with a nil diff report")
}
var err error
var query string
rpt, err := json.Marshal(dr)
if err != nil {
return err
}
if lastRow == nil {
query, err = sqlparser.ParseAndBind(sqlUpdateTableNoProgress,
sqltypes.Int64BindVariable(dr.ProcessedRows),
sqltypes.StringBindVariable(string(rpt)),
sqltypes.Int64BindVariable(td.wd.ct.id),
sqltypes.StringBindVariable(td.table.Name),
)
if err != nil {
return errView on GitHub (pinned to 01a25a7d17)
Solutions
- Ensure the diff pass always produces a non-nil DiffReport before calling updateTableProgress
- Guard the caller: skip the progress update when dr == nil instead of calling it
- Check for early-return paths in diff() that leave the report nil (e.g. zero rows compared) and set a default empty report
Example fix
// before
td.updateTableProgress(dbClient, dr, lastRow)
// after
if dr != nil {
td.updateTableProgress(dbClient, dr, lastRow)
} Defensive patterns
Strategy: type-guard
Type guard
func hasDiffReport(dr *DiffReport) bool { return dr != nil }
if !hasDiffReport(dr) { skipProgressUpdate() } Try / catch
if err := td.updateTableProgress(dbClient, dr, lastRow); err != nil {
log.Warn("progress update failed", slog.Any("error", err))
} Prevention
- Never call updateTableProgress without a populated DiffReport
- Check early-return paths in diff() that can leave the report nil
- Add nil-report tests when touching tableDiffer
When it happens
Trigger: The differ's diff() loop (or tests) calls updateTableProgress with a nil DiffReport pointer, typically when no report was produced for the table or a nil is passed from an anonymous helper.
Common situations: A vdiff run where the table produced no comparison result before a progress update was attempted; programming changes in custom/test code (TestUpdateTableProgressDropsQueryEcho) calling updateTableProgress directly.
Related errors
- VDiff not implemented in vtcombo
- there are no cells in the topo
- usage: VDiff -- <keyspace>.<workflow> %s [%s|<UUID>]
- invalid action '%s'; %s
- invalid --limit value (%d), maximum number of rows to compar
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/eb385f3b3f87e1ed.
Report an issue: GitHub.