fix(envd): distinguish expected stdin lifecycle errors from CodeInternal - #3628
Open
AdaAibaby wants to merge 1 commit into
Open
fix(envd): distinguish expected stdin lifecycle errors from CodeInternal#3628AdaAibaby wants to merge 1 commit into
AdaAibaby wants to merge 1 commit into
Conversation
AdaAibaby
requested review from
ValentaTomas,
dobrac and
jakubno
as code owners
September 8, 2026 04:00
Process.SendInput and CloseStdin previously mapped every WriteStdin /
WriteTty / CloseStdin failure to CodeInternal (CloseStdin to CodeUnknown).
Several of these are expected process-lifecycle outcomes, not envd faults:
- stdin was started disabled, or has already been closed;
- a PTY-backed process received stdin (input belongs on the pty);
- the process exited and its stdin read end is gone, so the write end
sees EPIPE / os.ErrClosed ("file already closed") / io.ErrClosedPipe.
Collapsing these into CodeInternal means clients cannot tell a normal
process-state transition from a genuine invariant failure, and it races
the Start response stream: SendInput can return CodeInternal before the
process's EndEvent arrives on the independent stream.
Introduce typed sentinels (ErrStdinUnavailable, ErrStdinOnPty,
ErrTtyUnavailable, ErrCloseStdinOnPty) and an InputErrorCode mapper in the
handler package, mirroring StartErrorCode:
- process selector missing -> CodeNotFound (already handled)
- process cannot accept stdin/pty -> CodeFailedPrecondition
- unexpected underlying I/O failure -> CodeInternal
CloseStdin follows the same taxonomy. The EndEvent remains the
authoritative exit signal; an input error alone is not the terminal
process result.
Callers that only check for a non-nil error are unaffected.
Fixes e2b-dev#3622
AdaAibaby
force-pushed
the
fix/envd-stdin-lifecycle-error-codes
branch
from
September 8, 2026 05:51
cc2f714 to
425e8ad
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Process.SendInputmaps everyHandler.WriteStdin/WriteTtyfailure toCodeInternal, andCloseStdinmaps every failure toCodeUnknown. Several of these are expected process-lifecycle outcomes rather than envd faults:EPIPE/os.ErrClosed(`file already closed`) /io.ErrClosedPipe.Because
CodeInternalis reserved for serious invariant failures, clients cannot distinguish a normal process-state transition from an unexpected envd failure. It also races the Start response stream:SendInputcan returnCodeInternalbefore the process'sEndEventarrives on the independent stream.Fixes #3622.
Fix
Introduce typed sentinels (
ErrStdinUnavailable,ErrStdinOnPty,ErrTtyUnavailable,ErrCloseStdinOnPty) and anInputErrorCodemapper in thehandlerpackage, mirroring the existingStartErrorCodepattern. The service layer (input.go) now maps input failures through it:CodeNotFound(already handled bygetProcess)CodeFailedPreconditionCodeInternalCloseStdinfollows the same taxonomy (previouslyCodeUnknown).The
EndEventremains the authoritative exit signal; aSendInputerror alone is not treated as the terminal process result.Note: after a process exits,
cmd.Wait()closes the stdin write end, so the observed error is often Go's `file already closed` (os.ErrClosed) rather than rawEPIPE— the mapper matches both, plusio.ErrClosedPipe/fs.ErrClosed.Compatibility
This changes observable Connect error codes. Callers that only check for a non-nil error are unaffected. Python/JS SDK behavior should be checked before merging, as flagged in the issue.
Tests
Added
input_error_test.go:InputErrorCodemapping (sentinels, wrapped sentinels, EPIPE/closed-pipe variants,EIO/opaque -> internal);connect.CodeOfassertions;WriteStdinreturns the typed sentinels for the disabled / pty-rejection preconditions;CodeFailedPrecondition, notCodeInternal.go build ./...,go vet,gofmt -l, and the full./internal/services/process/...suite all pass.