vitessio/vitess · error
executor is closed
Error message
executor is closed
What it means
TabletExecutor.Validate checks a list of SQL statements by parsing DDLs, but refuses to operate once the executor has been closed. isClosed is set after Close(), so any subsequent Validate call returns this error.
Source
Thrown at go/vt/schemamanager/tablet_executor.go:142
}
tabletInfo, err := exec.ts.GetTablet(ctx, shardInfo.PrimaryAlias)
if err != nil {
return fmt.Errorf("unable to get primary tablet info, keyspace: %s, shard: %s, error: %v", keyspace, shardName, err)
}
exec.tablets = append(exec.tablets, tabletInfo.Tablet)
}
if len(exec.tablets) == 0 {
return fmt.Errorf("keyspace: %s does not contain any primary tablets", keyspace)
}
exec.isClosed = false
return nil
}
// Validate validates a list of sql statements.
func (exec *TabletExecutor) Validate(ctx context.Context, sqls []string) error {
if exec.isClosed {
return errors.New("executor is closed")
}
if err := exec.parseDDLs(sqls); err != nil {
return err
}
return nil
}
func (exec *TabletExecutor) parseDDLs(sqls []string) error {
for _, sql := range sqls {
stmt, err := exec.parser.Parse(sql)
if err != nil {
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "failed to parse sql: %s, got error: %v", sql, err)
}
switch stmt.(type) {
case sqlparser.DDLStatement:
case sqlparser.DBDDLStatement:
case *sqlparser.RevertMigration:View on GitHub (pinned to 01a25a7d17)
Solutions
- Create a new TabletExecutor instance for validation after the previous one was closed
- Reorder lifecycle code so Validate happens before Close
- Check executor.isClosed (or track closure) before reusing the executor
Example fix
// before exec.Close() err := exec.Validate(ctx, sqls) // executor is closed // after err := exec.Validate(ctx, sqls) exec.Close()
Defensive patterns
Strategy: try-catch
Validate before calling
if exec.IsClosed() { exec = schemamanager.NewTabletExecutor(...) } Try / catch
if err != nil {
if err.Error() == "executor is closed" {
// recreate executor and retry once
}
} Prevention
- Use one TabletExecutor per lifecycle phase
- Call Close only after all Validate/Execute calls
- Avoid storing executors in long-lived state across runs
When it happens
Trigger: Calling Validate (or executing) on a TabletExecutor after Close() was called on it.
Common situations: Reusing an executor across schema-manager runs or retry logic without creating a new instance; lifecycle bugs where a deferred Close() runs before a later Validate.
Related errors
- UUID values must be unique
- --batch-size requires 'direct' ddl_strategy
- --batch-size conflicts with --uuid-list. Batching does not s
- --batch-size only allowed when all queries are CREATE TABLE|
- ErrStreamClosed
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/8d3d6b2b0a3d04ac.
Report an issue: GitHub.