From 519e1a6087a745bbe0169106fe1fbf585a6a0e48 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Fri, 10 Jul 2026 15:22:45 -0500 Subject: [PATCH] fix(tunnel): surface agent command failure instead of masking it ClassifyTunnelErrors discarded the tunnel (agent command) error whenever the handler side completed cleanly. When the in-container agent sent its result over gRPC and then failed a later stage (e.g. git clone), the handler saw a clean completion (nil) so the real exit error was dropped. `up` then proceeded to save the workspace result, found the directory already removed by the agent's failure cleanup, and reported the misleading "workspace X no longer exists" instead of the real cause. Surface a permanent tunnel error when the handler returns nil, reusing ClassifyError so normal shutdown/EOF completions still resolve to nil. --- pkg/tunnel/connerror.go | 3 +++ pkg/tunnel/connerror_test.go | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/tunnel/connerror.go b/pkg/tunnel/connerror.go index ddbe27179..0361a4801 100644 --- a/pkg/tunnel/connerror.go +++ b/pkg/tunnel/connerror.go @@ -38,6 +38,9 @@ func IsEOF(err error) bool { func ClassifyTunnelErrors(tunnelErr, handlerErr error) error { if handlerErr == nil { + if ClassifyError(tunnelErr) == ErrorPermanent { + return fmt.Errorf("agent command: %w", tunnelErr) + } return nil } if IsEOF(handlerErr) { diff --git a/pkg/tunnel/connerror_test.go b/pkg/tunnel/connerror_test.go index 176c77a0c..365c29031 100644 --- a/pkg/tunnel/connerror_test.go +++ b/pkg/tunnel/connerror_test.go @@ -72,7 +72,10 @@ func TestClassifyTunnelErrors(t *testing.T) { wantNil bool wantSubstr string }{ - {"handler nil", tunnelErr, nil, true, ""}, + {"handler nil, no tunnel err", nil, nil, true, ""}, + {"handler nil, permanent tunnel err", tunnelErr, nil, false, "agent command: dial failed"}, + {"handler nil, shutdown tunnel err", context.Canceled, nil, true, ""}, + {"handler nil, EOF tunnel err", io.EOF, nil, true, ""}, {"handler EOF with tunnel err", tunnelErr, io.EOF, false, "connect to server: dial failed"}, {"handler EOF no tunnel err", nil, io.EOF, true, ""}, {"handler non-EOF", nil, handlerNonEOF, false, "tunnel to container: handler broke"},