From 01c26dfeb332a9b8c57d6c45315a811a70bca7d7 Mon Sep 17 00:00:00 2001 From: Tom Enden Date: Tue, 31 Mar 2026 16:25:41 +0300 Subject: [PATCH] fix: kill ttyd process on early exit from Evaluate The deferred cleanup in Evaluate() only called v.close(), which closes the browser but not the ttyd process. Any early return between Start() and the teardown() call (e.g. SET command failure, dimension check, Hide block error) would leave an orphaned ttyd process running. Also clean up ttyd in Start() if browser launch or page creation fails. --- evaluator.go | 7 ++++++- vhs.go | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/evaluator.go b/evaluator.go index 5768b854..456103d5 100644 --- a/evaluator.go +++ b/evaluator.go @@ -42,7 +42,12 @@ func Evaluate(ctx context.Context, tape string, out io.Writer, opts ...Evaluator if err := v.Start(); err != nil { return []error{err} } - defer func() { _ = v.close() }() + defer func() { + _ = v.close() + if v.tty != nil && v.tty.Process != nil { + _ = v.tty.Process.Kill() + } + }() // Let's wait until we can access the window.term variable. // diff --git a/vhs.go b/vhs.go index 235e7b42..ad645a2b 100644 --- a/vhs.go +++ b/vhs.go @@ -140,11 +140,14 @@ func (vhs *VHS) Start() error { enableNoSandbox := os.Getenv("VHS_NO_SANDBOX") != "" u, err := launcher.New().Leakless(false).Bin(path).NoSandbox(enableNoSandbox).Launch() if err != nil { + _ = vhs.tty.Process.Kill() return fmt.Errorf("could not launch browser: %w", err) } browser := rod.New().ControlURL(u).MustConnect() page, err := browser.Page(proto.TargetCreateTarget{URL: fmt.Sprintf("http://localhost:%d", port)}) if err != nil { + browser.MustClose() + _ = vhs.tty.Process.Kill() return fmt.Errorf("could not open ttyd: %w", err) }