Go version
go version go1.23.0 darwin/arm64
Output of go env
in your module/workspace:
GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/bing/Library/Caches/go-build'
GOENV='/Users/bing/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/bing/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/bing/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.22.1'
GCCGO='gccgo'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/cl/v9c2b59x40ldmf9vr6hrx8mw0000gn/T/go-build3833267051=/tmp/go-build -gno-record-gcc-switches -fno-common'
What did you do?
I ran the following test code, expecting it to terminate the command as intended
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
if err := exec.CommandContext(ctx, "sleep", "5").Run(); err != nil {
fmt.Println(err)
}
}
What did you see happen?
In fact, it did terminate the command, but the returned error was signal: killed.
I don't think this is the correct error output.
What did you expect to see?
I believe it should return "context canceled,". Every developer clearly understands that the lifecycle of this command is managed by their own context, and the command would terminate due to the cancel of the context derived from their Go program.
However, "signal: killed" is an unexpected error, and it can be triggered for various reasons. This error makes the lifecycle management of the command unclear (at least to me). I'm not even sure why the command was terminated—maybe it was due to an OOM kill? Or perhaps it was manually terminated by the user? I might need to check the system logs to confirm this.
The relevant code can be found at https://github.com/golang/go/blob/7c72dc77a934cf5957e5667e204506a61457e66a/src/os/exec/exec.go#L928-L939
From the comments, I understand this is an expected error handling behavior. Should we revisit this behavior based on the points above for further discussion?
Comment From: gabyhelp
Related Issues and Documentation
- os/exec: CommandContext with timeout with multiple subprocesses isn't canceled #22485 (closed)
- os/exec: exec.CommandContext(...).Output() does not respect context timeout #57129 (closed)
- affected/package: os/exec panic with CommandContext #58828 (closed)
- os/exec: CommandContext does not forward the context's error on timeout #21880
- os/exec: exec.Cmd fails to cancel with non-*os.File outputs on linux #18874 (closed)
- os/signal: NotifyContext should communicate the signal via context.WithCancelCause #60733 (closed)
- affected/package: os/exec, context not work. #53700 (closed)
- proposal: os/exec: allow user of CommandContext to specify the kill signal when context is done #21135 (closed)
- os/exec: Unable to kill a command on macOS? #27440 (closed)
- os/exec: incorrect "fatal error: all goroutines are asleep - deadlock!" #41271 (closed)
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: seankhliao
I believe the current setup is appropriate, a timeout masks any more detailed error, and it may be that the process chooses to handle the termination signal gracefully so the error isn't always a plain killed.
Comment From: cptaffe
@seankhliao if the error signal: killed
was caused by a canceled context, shouldn't the canceled context be in the errors cause tree as reported by error.Is
? Currently this is not the case, so telling apart whether a process was killed because the context was canceled, or because of an external signal, is not possible.