@@ -27,6 +27,8 @@ import (
27
27
var (
28
28
// ErrTimeout is returned when a script times out.
29
29
ErrTimeout = xerrors .New ("script timed out" )
30
+ // ErrTimeout is returned when a script times out.
31
+ ErrOutputPipesOpen = xerrors .New ("script exited without closing output pipes" )
30
32
31
33
parser = cron .NewParser (cron .Second | cron .Minute | cron .Hour | cron .Dom | cron .Month | cron .DowOptional )
32
34
)
@@ -248,7 +250,21 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript)
248
250
err = cmdCtx .Err ()
249
251
case err = <- cmdDone :
250
252
}
251
- if errors .Is (err , context .DeadlineExceeded ) {
253
+ switch {
254
+ case errors .Is (err , exec .ErrWaitDelay ):
255
+ err = ErrOutputPipesOpen
256
+ message := fmt .Sprintf ("script exited successfully, but output pipes were not closed after %s" , cmd .WaitDelay )
257
+ details := fmt .Sprint (
258
+ "This usually means a child process was started with references to stdout or stderr. As a result, this " +
259
+ "process may now have been terminated. Consider using a separate \" coder_script\" for the service, " +
260
+ "see https://coder.com/docs/v2/latest/templates/troubleshooting#startup-script-issues for more information." ,
261
+ )
262
+ // Inform the user by propagating the message via log writers.
263
+ _ , _ = fmt .Fprintf (cmd .Stderr , "WARNING: %s. %s\n " , message , details )
264
+ // Also log to agent logs for ease of debugging.
265
+ r .Logger .Warn (ctx , message , slog .F ("details" , details ), slog .Error (err ))
266
+
267
+ case errors .Is (err , context .DeadlineExceeded ):
252
268
err = ErrTimeout
253
269
}
254
270
return err
0 commit comments