{"record":{"id":"c66a705e4d738a5c","repo":"junegunn/fzf","slug":"invalid-become-command","errorCode":null,"errorMessage":"invalid become command","messagePattern":"invalid become command","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/proxy.go","lineNumber":244,"sourceCode":"\tintChan := make(chan os.Signal, 1)\n\tdefer close(intChan)\n\tgo func() {\n\t\tif sig, valid := <-intChan; valid {\n\t\t\tcmd.Process.Signal(sig)\n\t\t}\n\t}()\n\tsignal.Notify(intChan, os.Interrupt)\n\tif err := cmd.Run(); err != nil {\n\t\tif exitError, ok := err.(*exec.ExitError); ok {\n\t\t\tcode := exitError.ExitCode()\n\t\t\tif code == ExitBecome {\n\t\t\t\tdata, err := os.ReadFile(becomeFile)\n\t\t\t\tos.Remove(becomeFile)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn ExitError, err\n\t\t\t\t}\n\t\t\t\tif len(data) == 0 {\n\t\t\t\t\treturn ExitError, errors.New(\"invalid become command\")\n\t\t\t\t}\n\t\t\t\telems := strings.Split(string(data), \"\\x00\")\n\t\t\t\tcommand := elems[0]\n\t\t\t\tenv := []string{}\n\t\t\t\tif len(elems) > 1 {\n\t\t\t\t\tenv = elems[1:]\n\t\t\t\t}\n\t\t\t\texecutor := util.NewExecutor(opts.WithShell)\n\t\t\t\tttyin, err := tui.TtyIn(opts.TtyDefault)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn ExitError, err\n\t\t\t\t}\n\t\t\t\tos.Remove(temp)\n\t\t\t\tos.Remove(input)\n\t\t\t\tos.Remove(output)\n\t\t\t\texecutor.Become(ttyin, env, command)\n\t\t\t}\n\t\t\treturn code, err","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/junegunn/fzf/blob/bd4efa277b49ef34ca4025bff9b1a288e1980eff/src/proxy.go#L226-L262","documentation":"The proxied command (the fzf invocation inside a shell wrapper, run via RunPrompt in proxy mode) exited with the special ExitBecome code, meaning it asked fzf to 'become' another process, but the file it uses to pass that command was empty. fzf pre-creates the become file with O_EXCL and the child process is supposed to write the NUL-separated command into it; zero bytes means the protocol was violated.","triggerScenarios":"Executing the become(...) action (or --become) inside a proxied/prompt context where the child writes nothing to the pre-created become file — e.g. a custom build or script emitting the ExitBecome exit code without the payload, or the write being interrupted.","commonSituations":"Custom fzf wrappers or plugins that mimic the become protocol incorrectly; a killed/timed-out child that exits with the magic code 130-ish ExitBecome by coincidence; extremely rare in stock usage.","solutions":["If you maintain the wrapper: after triggering become, always write the command string (NUL-separated argv) into the become file before exiting with the become code","If you are a plain user hitting this with stock fzf, report it upstream with the exact keybind/action config — it indicates a corrupted become handshake","As a workaround, replace --become with execute(...) plus shell-level exec in your keybind"],"exampleFix":"# before (broken wrapper)\nexit 130   # ExitBecome code but nothing written to become file\n# after\nprintf '%s\\0' \"$cmd\" > \"$become_file\"\nexit 130","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"# in Go, treat this as a fatal protocol error from RunPrompt\ncode, err := fzf.RunPrompt(...)\nif err != nil && strings.Contains(err.Error(), \"invalid become command\") {\n    // child violated the become handshake; do not retry, surface it\n    log.Fatalf(\"become handshake failed: %v\", err)\n}","preventionTips":["Always write the NUL-separated command to the become file before exiting with the become code","Use stock fzf binaries for become flows; the protocol is internal and version-specific"],"tags":["fzf","become","proxy-protocol","internal"],"backgroundTag":null,"analyzedSha":"bd4efa277b49ef34ca4025bff9b1a288e1980eff","analyzedAt":"2026-08-15T06:11:46.983Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}