go-delve/delve · error

terminated

Error message

terminated

What it means

During injected function-call evaluation, if the process exits (or terminates) while the call is in progress, the session detects it via processExited(state, err), emits exited/terminated events to the client, and then returns the sentinel error 'terminated' to the evaluate caller. It signals that the call never completed because the debuggee died.

Source

Thrown at service/dap/server.go:3403

	loadCfg := s.loadConfig()
	loadCfg.MaxStringLen = maxStringLenInCallRetVars

	// TODO(polina): since call will resume execution of all goroutines,
	// we should do this asynchronously and send a continued event to the
	// editor, followed by a stop event when the call completes.
	state, err := s.debugger.Command(&api.DebuggerCommand{
		Name:                 api.Call,
		ReturnInfoLoadConfig: api.LoadConfigFromProc(&loadCfg),
		Expr:                 expr,
		UnsafeCall:           false,
		GoroutineID:          int64(goid),
		WithEvents:           true,
	}, nil, s.conn.closedChan, s.convertDebuggerEvent)
	if processExited(state, err) {
		s.preTerminatedWG.Wait()
		s.sendExitedEvent(state, err)
		s.send(&dap.TerminatedEvent{Event: *s.newEvent("terminated")})
		return nil, nil, errors.New("terminated")
	}
	if err != nil {
		return nil, nil, err
	}

	// After the call is done, the goroutine where we injected the call should
	// return to the original stopped line with return values. However,
	// it is not guaranteed to be selected due to the possibility of the
	// of simultaneous breakpoints. Therefore, we check all threads.
	var retVars []*proc.Variable
	found := false
	for _, t := range state.Threads {
		if t.GoroutineID == stateBeforeCall.SelectedGoroutine.ID &&
			t.Line == stateBeforeCall.SelectedGoroutine.CurrentLoc.Line && t.CallReturn {
			found = true
			// The call completed. Get the return values.
			retVars, err = s.debugger.FindThreadReturnValues(t.ID, loadCfg)
			if err != nil {

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Do not evaluate calls that terminate the process (os.Exit, log.Fatal, panic)
  2. Re-launch the debuggee; the session is over once the process exits
  3. If calls near the end of main keep terminating, place breakpoints earlier or guard with runtime checks
  4. Inspect the call's side effects in a sandbox/test instead of evaluating them in the live target

Example fix

// before (debug console, terminates target)
os.Exit(1)
// after
fmt.Println("would exit")  // or set a breakpoint instead of calling exit
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-screen dangerous calls before injecting
blocked := []string{"os.Exit", "log.Fatal", "panic"}
for _, b := range blocked {
    if strings.HasPrefix(expr, b) { return fmt.Errorf("refusing %q", b) }
}

Try / catch

if err.Error() == "terminated" {
    // the debuggee exited: tear down the session and relaunch before any further requests
}

Prevention

When it happens

Trigger: The injected call causes the target process to exit (e.g. calling os.Exit or a function that panics fatally); the debuggee is killed externally during a call; the program finishes/naturally exits while a call is being injected at the end of main.

Common situations: Evaluating calls like os.Exit(1) or log.Fatal from the debug console; long-running call racing with program termination at the last breakpoint in main; crashes triggered by the injected call's side effects.

Related errors


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/8b113f70c6ad21b1. Report an issue: GitHub.