vitessio/vitess · error
unsupported multiple keyspace_id expressions: %v
Error message
unsupported multiple keyspace_id expressions: %v
What it means
The special keyspace_id() function in a filter select list must be called with zero arguments — the vstreamer itself supplies the row's keyspace id. Any argument count other than 0 (typically 1) is rejected with this error.
Source
Thrown at go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go:481
}, aliased.Expr)
if err != nil {
return nil, fmt.Errorf("failed to find column name for convert using expression: %v, %v", sqlparser.String(aliased.Expr), err)
}
selExpr := &sqlparser.ConvertUsingExpr{
Type: "utf8mb4",
Expr: &sqlparser.ColName{Name: colName},
}
cexpr.expr = expr
cexpr.operation = opExpr
tpb.sendSelect.AddSelectExpr(&sqlparser.AliasedExpr{Expr: selExpr, As: as})
cexpr.references[as.String()] = true
return cexpr, nil
}
if expr, ok := aliased.Expr.(*sqlparser.FuncExpr); ok {
switch fname := expr.Name.Lowered(); fname {
case "keyspace_id":
if len(expr.Exprs) != 0 {
return nil, fmt.Errorf("unsupported multiple keyspace_id expressions: %v", sqlparser.String(expr))
}
tpb.sendSelect.AddSelectExpr(&sqlparser.AliasedExpr{Expr: aliased.Expr})
// The vstreamer responds with "keyspace_id" as the field name for this request.
cexpr.expr = &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("keyspace_id")}
return cexpr, nil
}
}
if expr, ok := aliased.Expr.(sqlparser.AggrFunc); ok {
if sqlparser.IsDistinct(expr) {
return nil, fmt.Errorf("unsupported distinct expression usage: %v", sqlparser.String(expr))
}
switch fname := expr.AggrName(); fname {
case "count":
if _, ok := expr.(*sqlparser.CountStar); !ok {
return nil, fmt.Errorf("only count(*) is supported: %v", sqlparser.String(expr))
}
cexpr.operation = opCountView on GitHub (pinned to 01a25a7d17)
Solutions
- Change the call to bare `keyspace_id()` with no arguments
- If you need the value of a vindex column, select the column itself instead
- Re-run the workflow after fixing the select expression
Example fix
// before SELECT keyspace_id(user_id) AS keyspace_id FROM t // after SELECT keyspace_id() AS keyspace_id FROM t
Defensive patterns
Strategy: validation
Validate before calling
for _, e := range sel.SelectExprs {
fe, ok := e.(*sqlparser.FuncExpr)
if !ok || fe.Name.Lowered() != "keyspace_id" { continue }
if len(fe.Exprs) != 0 {
return errors.New("keyspace_id() takes no arguments")
}
} Type guard
func isBareKeyspaceID(e sqlparser.SelectExpr) bool {
ae, ok := e.(*sqlparser.AliasedExpr)
if !ok { return false }
fe, ok := ae.Expr.(*sqlparser.FuncExpr)
return ok && fe.Name.Lowered() == "keyspace_id" && len(fe.Exprs) == 0
} Try / catch
if err != nil && strings.Contains(err.Error(), "keyspace_id") {
return fmt.Errorf("use keyspace_id() with no arguments: %w", err)
} Prevention
- Always call keyspace_id() with zero arguments
- Remember vstreamer supplies the keyspace id; never pass a column
- Add a lint rule for filter SQL function arity
When it happens
Trigger: A filter rule contains `keyspace_id(some_col)` or `keyspace_id(1)` instead of the bare `keyspace_id()`.
Common situations: Users assuming keyspace_id() takes a column argument; generators emitting vindex-style calls; confusion with similar functions in other systems.
Related errors
- rule %v does not have a select expression in vreplication
- unexpected rule in vreplication: %v
- table %s not found in schema
- more than one target for source table %s: %s and %s
- filter rules are not supported for SBR replication: %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/72716e0f00b3c6a3.
Report an issue: GitHub.