vitessio/vitess · error
rule %v does not have a select expression in vreplication
Error message
rule %v does not have a select expression in vreplication
What it means
After classifying a rule's table, templatizeRule inspects rule.Filter to templatize its keyrange or SELECT expression. If the filter is an empty string there is no select expression to work with, so the rule cannot be migrated and this error is thrown. A vreplication rule must carry either a keyrange or a select expression as its filter.
Source
Thrown at go/vt/vtctl/workflow/stream_migrator.go:1124
return shardedStreams, nil
}
// templatizeRule replaces keyrange values with {{.}}.
// This can then be used by go's template package to substitute other keyrange values.
func (sm *StreamMigrator) templatizeRule(ctx context.Context, rule *binlogdatapb.Rule) (StreamType, error) {
vtable, ok := sm.ts.SourceKeyspaceSchema().Tables[rule.Match]
if !ok && !schema.IsInternalOperationTableName(rule.Match) {
return StreamTypeUnknown, fmt.Errorf("table %v not found in vschema", rule.Match)
}
if vtable != nil && vtable.Type == vindexes.TypeReference {
return StreamTypeReference, nil
}
switch {
case rule.Filter == "":
return StreamTypeUnknown, fmt.Errorf("rule %v does not have a select expression in vreplication", rule)
case key.IsValidKeyRange(rule.Filter):
rule.Filter = "{{.}}"
return StreamTypeSharded, nil
case rule.Filter == vreplication.ExcludeStr:
return StreamTypeUnknown, fmt.Errorf("unexpected rule in vreplication: %v", rule)
default:
if err := sm.templatizeKeyRange(ctx, rule); err != nil {
return StreamTypeUnknown, err
}
return StreamTypeSharded, nil
}
}
func (sm *StreamMigrator) templatizeKeyRange(ctx context.Context, rule *binlogdatapb.Rule) error {
statement, err := sm.parser.Parse(rule.Filter)
if err != nil {
return errView on GitHub (pinned to 01a25a7d17)
Solutions
- Inspect the stream's filter (`select id, workflow, source from _vt.vreplication`) and fix the rule so each has a valid keyrange or SELECT filter.
- Cancel and recreate the workflow using MoveTables/Materialize so rules are generated correctly.
- If rows were hand-edited or written by external tooling, stop doing so — always create/modify streams via vtctldclient.
- Check the Vitess version that created the rows for format changes between versions.
Example fix
// before: {match: "user", filter: ""}
// after: {match: "user", filter: "select * from user where in_keyrange(-40)"} — or recreate the workflow Defensive patterns
Strategy: validation
Validate before calling
-- Each rule must have a non-empty filter (keyrange or SELECT) SELECT id, workflow FROM _vt.vreplication WHERE JSON_EXTRACT(source, '$.filter.rules[*].filter') IS NULL OR JSON_SEARCH(source, 'one', '') IS NOT NULL;
Type guard
func ruleHasFilter(r *binlogdatapb.Rule) bool { return r != nil && r.Filter != "" } Prevention
- Create and modify vreplication rules only via vtctldclient, never direct SQL.
- Validate generated filter rules after any custom tooling touches workflows.
When it happens
Trigger: templatize -> templatizeRule encountering a Rule whose Filter field is "" — e.g. a manually inserted/edited _vt.vreplication row or a filter rule constructed programmatically without a filter, then a stream migration (BuildStreamMigrator/StopStreams) runs.
Common situations: Hand-edited _vt.vreplication rows; custom tooling that wrote filter rules with an empty filter; workflow created by an older Vitess version writing rules in a format the current code rejects; corrupted binlog source proto.
Related errors
- no binlog source is defined for workflow %s
- unexpected rule in vreplication: %v
- both atomic copy and partial mode cannot be specified for th
- invalid workflow
- multiple source keyspaces for a single workflow
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/6062e3726aaebcb1.
Report an issue: GitHub.