{"record":{"id":"ba84e83883dd95f5","repo":"vitessio/vitess","slug":"no-function-closure-for-event-stream-specified","errorCode":null,"errorMessage":"no function closure for Event stream specified","messagePattern":"no function closure for Event stream specified","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtctl/vtctlclient/wrapper.go","lineNumber":36,"sourceCode":"\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\tlogutilpb \"vitess.io/vitess/go/vt/proto/logutil\"\n)\n\nvar defaultTimeout = time.Hour\n\n// RunCommandAndWait executes a single command on a given vtctld and blocks until the command did return or timed out.\n// Output from vtctld is streamed as logutilpb.Event messages which\n// have to be consumed by the caller who has to specify a \"recv\" function.\nfunc RunCommandAndWait(ctx context.Context, server string, args []string, recv func(*logutilpb.Event)) error {\n\tif recv == nil {\n\t\treturn errors.New(\"no function closure for Event stream specified\")\n\t}\n\t// create the client\n\tclient, err := New(ctx, server)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot dial to server %v: %v\", server, err)\n\t}\n\tdefer client.Close()\n\n\t// run the command ( get the timeout from the context )\n\ttimeout := defaultTimeout\n\tdeadline, ok := ctx.Deadline()\n\tif ok {\n\t\ttimeout = time.Until(deadline)\n\t}\n\tstream, err := client.ExecuteVtctlCommand(ctx, args, timeout)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot execute remote command: %v\", err)\n\t}","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtctl/vtctlclient/wrapper.go#L18-L54","documentation":"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.","triggerScenarios":"Calling vtctldclient.RunCommandAndWait(ctx, server, args, nil) — i.e. omitting the recv closure — from main, runLegacyCommand, or wrappers like assertColumnVindex/applyVschema/reloadSchemaKeyspace.","commonSituations":"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.","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"],"exampleFix":"// before\nerr := vtctldclient.RunCommandAndWait(ctx, server, args, nil)\n// after\nerr := vtctldclient.RunCommandAndWait(ctx, server, args, func(e *logutilpb.Event) {\n    fmt.Println(e.Value)\n})","handlingStrategy":"validation","validationCode":"if recv == nil {\n    return fmt.Errorf(\"recv callback required by RunCommandAndWait\")\n}","typeGuard":"func recvNotNil(recv func(*logutilpb.Event)) bool { return recv != nil }","tryCatchPattern":"if err := vtctldclient.RunCommandAndWait(ctx, server, args, recv); err != nil {\n    if strings.Contains(err.Error(), \"no function closure\") {\n        err = fmt.Errorf(\"programming error: %w\", err)\n    }\n    return err\n}","preventionTips":["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"],"tags":["vtctldclient","grpc","callback","invalid-argument"],"backgroundTag":"nil-callback-argument","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}