@@ -64,6 +64,11 @@ func (e *Environment) Attach(ctx context.Context) error {
6464 e .SetStream (& st )
6565 }
6666
67+ // Create a channel to signal when the reader goroutine has started.
68+ // This prevents a race condition where the container starts producing output
69+ // before the goroutine begins reading, causing initial console output to be lost.
70+ readerStarted := make (chan struct {})
71+
6772 go func () {
6873 // Don't use the context provided to the function, that'll cause the polling to
6974 // exit unexpectedly. We want a custom context for this, the one passed to the
@@ -86,6 +91,10 @@ func (e *Environment) Attach(ctx context.Context) error {
8691 }
8792 }()
8893
94+ // Signal that the reader goroutine has started and is about to begin reading.
95+ // This must be done before ScanReader is called to ensure proper synchronization.
96+ close (readerStarted )
97+
8998 if err := system .ScanReader (e .stream .Reader , func (v []byte ) {
9099 e .logCallbackMx .Lock ()
91100 defer e .logCallbackMx .Unlock ()
@@ -96,7 +105,17 @@ func (e *Environment) Attach(ctx context.Context) error {
96105 }
97106 }()
98107
99- return nil
108+ // Wait for the reader goroutine to start before returning. This ensures that
109+ // when the container starts immediately after this function returns, the output
110+ // reader is already active and won't miss any initial console output.
111+ //
112+ // Use a select with the context to avoid hanging indefinitely if something goes wrong.
113+ select {
114+ case <- readerStarted :
115+ return nil
116+ case <- ctx .Done ():
117+ return errors .WrapIf (ctx .Err (), "environment/docker: context canceled while waiting for reader to start" )
118+ }
100119}
101120
102121// InSituUpdate performs an in-place update of the Docker container's resource
0 commit comments