{"record":{"id":"e27409b8444debf7","repo":"vitessio/vitess","slug":"lastpk-for-table-s-has-no-fields-defined","errorCode":null,"errorMessage":"lastpk for table %s has no fields defined","messagePattern":"lastpk for table (.+?) has no fields defined","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vttablet/tabletserver/vstreamer/uvstreamer.go","lineNumber":154,"sourceCode":"\t\tthrottlerApp: throttlerApp,\n\t\toptions:      options,\n\t}\n\n\treturn uvs\n}\n\n// buildTablePlan identifies the tables for the copy phase and creates the plans which consist of the lastPK seen\n// for a table and its Rule (for filtering purposes by the vstreamer engine)\n// it can be called\n//\n//\tthe first time, with just the filter and an empty pos\n//\tduring a restart, with both the filter and list of TableLastPK from the vgtid\nfunc (uvs *uvstreamer) buildTablePlan() error {\n\tuvs.plans = make(map[string]*tablePlan)\n\ttableLastPKs := make(map[string]*binlogdatapb.TableLastPK)\n\tfor _, tablePK := range uvs.inTablePKs {\n\t\tif tablePK != nil && tablePK.Lastpk != nil && len(tablePK.Lastpk.Fields) == 0 {\n\t\t\treturn fmt.Errorf(\"lastpk for table %s has no fields defined\", tablePK.TableName)\n\t\t}\n\t\ttableLastPKs[tablePK.TableName] = tablePK\n\t}\n\ttables := uvs.se.GetSchema()\n\tfor range tables {\n\t\tfor _, rule := range uvs.filter.Rules {\n\t\t\tif !strings.HasPrefix(rule.Match, \"/\") {\n\t\t\t\t_, ok := tables[rule.Match]\n\t\t\t\tif !ok {\n\t\t\t\t\treturn fmt.Errorf(\"table %s is not present in the database\", rule.Match)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Set of tables to copy during the copy phase. Only if we need to copy\n\t// specific tables, else keep it nil if we need to copy every table.\n\tvar tablesToCopySet sets.Set[string]","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vttablet/tabletserver/vstreamer/uvstreamer.go#L136-L172","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"for _, tp := range tablePKs {\n    if tp.Lastpk != nil && len(tp.Lastpk.Fields) == 0 {\n        return fmt.Errorf(\"rejecting malformed lastpk for %s: fields empty\", tp.TableName)\n    }\n}","typeGuard":null,"tryCatchPattern":"err := uvstream(ctx, filter, tablePKs)\nif err != nil && strings.Contains(err.Error(), \"has no fields defined\") {\n    // checkpoint is malformed: clear it and restart copy phase\n    clearTableLastPK(workflow, errTableName)\n    restart(ctx)\n}","preventionTips":["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"],"tags":["uvstreamer","lastpk","checkpoint"],"backgroundTag":"malformed-lastpk-checkpoint","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}