vitessio/vitess · error
lastpk for table %s has no fields defined
Error message
lastpk for table %s has no fields defined
What it means
uvstreamer.buildTablePlan validates every incoming TableLastPK: if a lastpk is present but carries an empty Fields list, the checkpoint is malformed and cannot be mapped to columns, so the unified vstreamer refuses to build the table plan.
Source
Thrown at go/vt/vttablet/tabletserver/vstreamer/uvstreamer.go:154
throttlerApp: throttlerApp,
options: options,
}
return uvs
}
// buildTablePlan identifies the tables for the copy phase and creates the plans which consist of the lastPK seen
// for a table and its Rule (for filtering purposes by the vstreamer engine)
// it can be called
//
// the first time, with just the filter and an empty pos
// during a restart, with both the filter and list of TableLastPK from the vgtid
func (uvs *uvstreamer) buildTablePlan() error {
uvs.plans = make(map[string]*tablePlan)
tableLastPKs := make(map[string]*binlogdatapb.TableLastPK)
for _, tablePK := range uvs.inTablePKs {
if tablePK != nil && tablePK.Lastpk != nil && len(tablePK.Lastpk.Fields) == 0 {
return fmt.Errorf("lastpk for table %s has no fields defined", tablePK.TableName)
}
tableLastPKs[tablePK.TableName] = tablePK
}
tables := uvs.se.GetSchema()
for range tables {
for _, rule := range uvs.filter.Rules {
if !strings.HasPrefix(rule.Match, "/") {
_, ok := tables[rule.Match]
if !ok {
return fmt.Errorf("table %s is not present in the database", rule.Match)
}
}
}
}
// Set of tables to copy during the copy phase. Only if we need to copy
// specific tables, else keep it nil if we need to copy every table.
var tablesToCopySet sets.Set[string]View on GitHub (pinned to 01a25a7d17)
Solutions
- Inspect the checkpointed TableLastPK for the named table; the lastpk proto must include both Fields and Rows.
- Reset the copy phase / clear the bad TableLastPK entry and let the streamer regenerate the checkpoint from live schema.
- If produced by an upgrade, check release notes for checkpoint-format migration and re-initialize the workflow.
Defensive patterns
Strategy: validation
Validate before calling
for _, tp := range tablePKs {
if tp.Lastpk != nil && len(tp.Lastpk.Fields) == 0 {
return fmt.Errorf("rejecting malformed lastpk for %s: fields empty", tp.TableName)
}
} Try / catch
err := uvstream(ctx, filter, tablePKs)
if err != nil && strings.Contains(err.Error(), "has no fields defined") {
// checkpoint is malformed: clear it and restart copy phase
clearTableLastPK(workflow, errTableName)
restart(ctx)
} Prevention
- Only produce TableLastPK via vitess APIs, not hand-built protos
- Validate checkpoints after workflow migrations/upgrades
- Clear-and-regenerate checkpoints instead of patching them
- Test restart flows after version upgrades
When it happens
Trigger: VStream called with a vgtid/tablePKs list where a table's Lastpk is non-nil but Lastpk.Fields is empty — a corrupt or hand-constructed checkpoint.
Common situations: Restarts where a workflow was migrated from an older format, checkpoints written by buggy/older code, or manual construction of TableLastPK when calling VStream APIs directly.
Related errors
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/e27409b8444debf7.
Report an issue: GitHub.