vitessio/vitess · error
no function closure for Event stream specified
Error message
no function closure for Event stream specified
What it means
vtctldclient's RunCommandAndWait streams vtctld output as logutilpb.Event messages and requires the caller to supply a recv callback to consume that stream. Calling it with a nil recv function makes output delivery impossible, so it returns this error before even dialing the server.
Source
Thrown at go/vt/vtctl/vtctlclient/wrapper.go:36
import (
"context"
"errors"
"fmt"
"io"
"time"
logutilpb "vitess.io/vitess/go/vt/proto/logutil"
)
var defaultTimeout = time.Hour
// RunCommandAndWait executes a single command on a given vtctld and blocks until the command did return or timed out.
// Output from vtctld is streamed as logutilpb.Event messages which
// have to be consumed by the caller who has to specify a "recv" function.
func RunCommandAndWait(ctx context.Context, server string, args []string, recv func(*logutilpb.Event)) error {
if recv == nil {
return errors.New("no function closure for Event stream specified")
}
// create the client
client, err := New(ctx, server)
if err != nil {
return fmt.Errorf("cannot dial to server %v: %v", server, err)
}
defer client.Close()
// run the command ( get the timeout from the context )
timeout := defaultTimeout
deadline, ok := ctx.Deadline()
if ok {
timeout = time.Until(deadline)
}
stream, err := client.ExecuteVtctlCommand(ctx, args, timeout)
if err != nil {
return fmt.Errorf("cannot execute remote command: %v", err)
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Pass a non-nil recv function, e.g. func(e *logutilpb.Event) { log.Info(e) }
- If you don't care about output, pass a no-op function instead of nil
- Use the plain RunCommand (non-streaming) if you don't need event streaming
Example fix
// before
err := vtctldclient.RunCommandAndWait(ctx, server, args, nil)
// after
err := vtctldclient.RunCommandAndWait(ctx, server, args, func(e *logutilpb.Event) {
fmt.Println(e.Value)
}) Defensive patterns
Strategy: validation
Validate before calling
if recv == nil {
return fmt.Errorf("recv callback required by RunCommandAndWait")
} Type guard
func recvNotNil(recv func(*logutilpb.Event)) bool { return recv != nil } Try / catch
if err := vtctldclient.RunCommandAndWait(ctx, server, args, recv); err != nil {
if strings.Contains(err.Error(), "no function closure") {
err = fmt.Errorf("programming error: %w", err)
}
return err
} Prevention
- Never pass nil for streaming callbacks; use a no-op func if output is unwanted
- Keep recv wiring in one shared helper so it can't be dropped in refactors
- Prefer the non-streaming RunCommand when events aren't needed
When it happens
Trigger: Calling vtctldclient.RunCommandAndWait(ctx, server, args, nil) — i.e. omitting the recv closure — from main, runLegacyCommand, or wrappers like assertColumnVindex/applyVschema/reloadSchemaKeyspace.
Common situations: Writing custom tooling against the vtctldclient package and passing nil for the callback because output wasn't needed; refactoring code that dropped the callback parameter.
Related errors
- gRPCVtctldClient in a SHUTDOWN state
- gRPC connection wait time exceeded
- failed to load static auth plugin. Plugin configured but grp
- the <cell> argument is required for the UpdateCellInfo comma
- the <cell> argument is required for the DeleteCellInfo comma
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/ba84e83883dd95f5.
Report an issue: GitHub.