go-delve/delve · error

Process %d has exited with status %d

Error message

Process %d has exited with status %d

What it means

exitedToError converts an api.DebuggerState that reports Exited into an error, because most stepping commands (step, stepInstruction, next, stepout, call) cannot proceed on a dead process. When the RPC returns no error but state.Exited is true, Delve surfaces 'Process %d has exited with status %d' instead of returning a state that callers cannot act on.

Source

Thrown at pkg/terminal/command.go:1457

		case "f", "c", "s":
			return answer, nil
		}
	}
}

func scopePrefixSwitch(t *Term, ctx callContext) error {
	if ctx.Scope.GoroutineID > 0 {
		_, err := t.client.SwitchGoroutine(ctx.Scope.GoroutineID)
		if err != nil {
			return err
		}
	}
	return nil
}

func exitedToError(state *api.DebuggerState, err error) (*api.DebuggerState, error) {
	if err == nil && state.Exited {
		return nil, fmt.Errorf("Process %d has exited with status %d", state.Pid, state.ExitStatus)
	}
	return state, err
}

func (c *Commands) step(t *Term, ctx callContext, args string) error {
	if err := scopePrefixSwitch(t, ctx); err != nil {
		return err
	}
	c.frame = 0
	stepfn := t.client.Step
	if ctx.Prefix == revPrefix {
		stepfn = t.client.ReverseStep
	}
	state, err := exitedToError(stepfn())
	if err != nil {
		printcontextNoState(t)
		return err
	}

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Check state.Exited (or run `exitStatus`) before issuing further stepping commands.
  2. Restart the session (`restart` or relaunch) before continuing to debug.
  3. Set a breakpoint near program end if you need to inspect state before termination.
  4. Wrap scripted command sequences to stop on this error rather than continuing to step.

Example fix

// before
state, err := client.GetState()
// then blindly: cmd.step(...)

// after
state, err := client.GetState()
if err == nil && state.Exited {
    fmt.Println("process already exited, restart first")
    return
}
// then: cmd.step(...)
Defensive patterns

Strategy: type-guard

Validate before calling

// Before stepping, confirm the process is still alive
state, err := client.GetState()
if err != nil {
    return err
}
if state.Exited {
    return fmt.Errorf("process %d already exited (status %d); restart before stepping", state.Pid, state.ExitStatus)
}

Type guard

func processAlive(s *api.DebuggerState) bool {
    return s != nil && !s.Exited
}

Try / catch

state, err := cmd.step(...)
if err != nil && strings.Contains(err.Error(), "has exited with status") {
    // recover: restart or stop the scripted session
    fmt.Fprintln(os.Stderr, err)
    return errStopSession
}

Prevention

When it happens

Trigger: Any of step, next, stepout, stepInstruction, call executed after the debugged program has terminated (e.g. the program ran to completion or called os.Exit), so the returned state has Exited=true with ExitStatus set.

Common situations: Hitting Enter on `next` repeatedly until the program finishes, then stepping once more; a `call` expression terminates the process; scripted terminal sessions blindly issue step commands after the program exits.

Related errors


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