{"record":{"id":"36fb83a2e5983fba","repo":"wavetermdev/waveterm","slug":"failed-to-build-shell-command-w","errorCode":null,"errorMessage":"failed to build shell command: %w","messagePattern":"failed to build shell command: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/genconn/ssh-impl.go","lineNumber":70,"sourceCode":"\t\tlock:    &sync.Mutex{},\n\t\tonce:    &sync.Once{},\n\t\tcmdSpec: cmdSpec,\n\t\tsession: session,\n\t}, nil\n}\n\n// Start begins execution of the command\nfunc (s *SSHProcessController) Start() error {\n\ts.lock.Lock()\n\tdefer s.lock.Unlock()\n\n\tif s.started {\n\t\treturn fmt.Errorf(\"command already started\")\n\t}\n\n\tfullCmd, err := BuildShellCommand(s.cmdSpec)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to build shell command: %w\", err)\n\t}\n\t// if stdout/stderr weren't piped, then session.stdout/stderr will be nil\n\t// and the library guarantees that the outputs will be attached to io.Discard\n\t// if stdin hasn't been piped, then session.stdin will be nil\n\t// and the libary guarantees that it will be attached to an empty bytes.Buffer, which will produce an immediate EOF\n\t// tl;dr we don't need to worry about hanging beause of long input or explicitly closing stdin\n\tif err := s.session.Start(fullCmd); err != nil {\n\t\treturn fmt.Errorf(\"failed to start command: %w\", err)\n\t}\n\ts.started = true\n\treturn nil\n}\n\n// Wait waits for the command to complete\nfunc (s *SSHProcessController) Wait() error {\n\ts.once.Do(func() {\n\t\ts.waitErr = s.session.Wait()\n\t})","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/genconn/ssh-impl.go#L52-L88","documentation":"SSHProcessController.Start builds the final shell command string from the stored CommandSpec via BuildShellCommand before handing it to the SSH session. If the CommandSpec is structurally invalid (BuildShellCommand returns an error), Start wraps and returns it as 'failed to build shell command'. The command never reaches the remote host.","triggerScenarios":"Calling Start() on an SSHProcessController whose CommandSpec cannot be rendered by BuildShellCommand — e.g. an invalid command spec configuration passed to MakeSSHCmdClient/MakeProcessController (bad shell type, empty/invalid command field).","commonSituations":"Passing a CommandSpec with an unsupported shell value; constructing a spec programmatically with an empty Command; a spec deserialized from config/JSON missing required fields.","solutions":["Inspect the wrapped error from BuildShellCommand to see which spec field is invalid.","Validate the CommandSpec before creating the controller (non-empty command, known shell).","Fix the spec construction site (config file, JSON deserialization, or literal)."],"exampleFix":"// before\nspec := genconn.CommandSpec{Shell: \"powershell\"} // on a linux remote\nctrl, _ := sshShellClient.MakeProcessController(spec)\nerr := ctrl.Start() // failed to build shell command\n// after\nspec := genconn.CommandSpec{Command: \"ls -la\", Shell: \"bash\"}\nctrl, err := sshShellClient.MakeProcessController(spec)\nif err != nil { return err }\nif err := ctrl.Start(); err != nil { return err }","handlingStrategy":"validation","validationCode":"func validSpec(s genconn.CommandSpec) error {\n    if s.Command == \"\" { return errors.New(\"CommandSpec.Command is empty\") }\n    switch s.Shell { case \"\", \"bash\", \"sh\", \"zsh\", \"cmd\", \"powershell\": return nil\n    default: return fmt.Errorf(\"unsupported shell %q\", s.Shell) }\n}\n// call before MakeSSHCmdClient/Start\nif err := validSpec(spec); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := ctrl.Start(); err != nil {\n    var buildErr error\n    errors.As(err, &buildErr) // log full wrapped chain\n    return fmt.Errorf(\"start failed (check CommandSpec): %w\", err)\n}","preventionTips":["Validate CommandSpec fields (non-empty Command, supported Shell) before constructing controllers.","Never build CommandSpec by hand-editing raw strings; use constructors or validated config.","Add a unit test covering every shell value your app can put into CommandSpec.","Log the full wrapped error chain (%w / errors.Unwrap) to pinpoint the bad field."],"tags":["ssh","command-spec","validation"],"backgroundTag":"invalid-command-spec","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}