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

  1. Pass a non-nil recv function, e.g. func(e *logutilpb.Event) { log.Info(e) }
  2. If you don't care about output, pass a no-op function instead of nil
  3. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/ba84e83883dd95f5. Report an issue: GitHub.