vitessio/vitess · error · ErrUnpreparedQuery
attempted to execute unprepared query
Error message
attempted to execute unprepared query
What it means
ErrUnpreparedQuery is returned when a QueryPlan is executed without first being prepared/bound. The VExec execution path (Execute, ExecuteScatter) expects a plan that went through preparation; executing a nil or unbound plan is a programming error and surfaces as this sentinel.
Source
Thrown at go/vt/vtctl/workflow/vexec/query_planner.go:46
var ( // Query planning errors.
// ErrCannotUpdateImmutableColumn is returned when attempting to plan a
// query that updates a column that should be treated as immutable.
ErrCannotUpdateImmutableColumn = errors.New("cannot update immutable column")
// ErrUnsupportedQueryConstruct is returned when a particular query
// construct is unsupported by a QueryPlanner, despite the more general kind
// of query being supported.
//
// For example, VReplication supports DELETEs, but does not support DELETEs
// with LIMIT clauses, so planning a "DELETE ... LIMIT" will return
// ErrUnsupportedQueryConstruct rather than a "CREATE TABLE", which would
// return an ErrUnsupportedQuery.
ErrUnsupportedQueryConstruct = errors.New("unsupported query construct")
)
// Query execution errors.
// ErrUnpreparedQuery is returned when attempting to execute an unprepared
// QueryPlan.
var ErrUnpreparedQuery = errors.New("attempted to execute unprepared query")
// QueryPlanner defines the interface that VExec uses to build QueryPlans for
// various vexec workflows. A given vexec table, which is to say a table in the
// "_vt" database, will have at most one QueryPlanner implementation, which is
// responsible for defining both what queries are supported for that table, as
// well as how to build plans for those queries.
//
// VReplicationQueryPlanner is a good example implementation to refer to.
type QueryPlanner interface {
// (NOTE:@ajm188) I don't think this method fits on the query planner. To
// me, especially given that it's only implemented by the vrep query planner
// in the old implementation (the schema migration query planner no-ops this
// method), this fits better on our workflow.Manager struct, probably as a
// method called something like "VReplicationExec(ctx, query, Options{DryRun: true})"
// DryRun(ctx context.Context) error
// PlanQuery constructs and returns a QueryPlan for a given statement. The
// resulting QueryPlan is suitable for repeated, concurrent use.View on GitHub (pinned to 01a25a7d17)
Solutions
- Check and handle the error from the planning step before calling Execute/ExecuteScatter
- Ensure the QueryPlan was fully prepared (non-nil bound query/fields) before execution
- Fix the planner implementation so it either returns a valid plan or an explicit unsupported-query error
Example fix
// before
plan, _ := planner.PlanDelete(stmt)
qp.Execute(ctx, plan) // ErrUnpreparedQuery
// after
plan, err := planner.PlanDelete(stmt)
if err != nil {
return err
}
return qp.Execute(ctx, plan) Defensive patterns
Strategy: validation
Validate before calling
if plan == nil || plan.BoundQuery == nil {
return ErrUnpreparedQuery
} Type guard
func isPrepared(p *QueryPlan) bool {
return p != nil && p.BoundQuery != nil && p.BoundQuery.Query != ""
} Try / catch
plan, err := planner.PlanUpdate(stmt)
if err != nil { return err }
if !isPrepared(plan) { return ErrUnpreparedQuery }
return qp.Execute(ctx, plan) Prevention
- Always propagate the planning error before executing
- Assert plans are non-nil in planner unit tests
- Bind queries at construction time so an unprepared plan is unrepresentable
When it happens
Trigger: Calling QueryPlan.Execute or ExecuteScatter before the plan was prepared (e.g. planner returned an error that was ignored, or a plan struct with a nil bound query is passed in).
Common situations: Bugs in new QueryPlanner implementations, ignoring the error from planDelete/planUpdate and proceeding to execute, or race conditions clearing prepared plans before execution.
Related errors
- cannot update immutable column
- unsupported query construct
- no primary found for shard
- table not supported by vexec
- value out of range
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/9ae8a7741e04cb5e.
Report an issue: GitHub.