go-delve/delve · error

internal debugger error: could not undo injected call during

Error message

internal debugger error: could not undo injected call during error recovery, original error: %v

What it means

When the eval program fails (stack.err != nil) with active call injections, run() must undo the injected calls before returning. If the topmost injection (fncall) is already the last retired call (stack.lastRetiredFncall), it cannot be undone safely, so the original error is replaced with this internal debugger error, preserving the original error as %v. It signals the undo path of the call-injection protocol hit an unrecoverable state.

Source

Thrown at pkg/proc/eval.go:1043

		if stack.callInjectionContinue && stack.err == nil {
			scope.callCtx.injectionThread = nil
			return
		}
	}

	if stack.err == nil && len(stack.fncalls) > 0 {
		stack.err = fmt.Errorf("internal debugger error: eval program finished without error but %d call injections still active", len(stack.fncalls))
		return
	}

	// If there is an error we must undo all currently executing call
	// injections before returning.

	if len(stack.fncalls) > 0 {
		fncallLog("undoing calls (%v)", stack.err)
		fncall := stack.fncallPeek()
		if fncall == stack.lastRetiredFncall {
			stack.err = fmt.Errorf("internal debugger error: could not undo injected call during error recovery, original error: %v", stack.err)
			return
		}
		if fncall.undoInjection != nil {
			if fncall.undoInjection.doComplete2 {
				// doComplete2 is set if CallInjectionComplete{DoPinning: true} has been
				// executed but CallInjectionComplete2 hasn't.
				regs, err := curthread.Registers()
				if err == nil {
					callInjectionComplete2(scope, scope.BinInfo, fncall, regs, curthread)
				}
			} else {
				// undoInjection is set if evalop.CallInjectionSetTarget has been
				// executed but evalop.CallInjectionComplete hasn't, we must undo the callOP
				// call in evalop.CallInjectionSetTarget before continuing.
				switch scope.BinInfo.Arch.Name {
				case "amd64":
					regs, _ := curthread.Registers()
					setSP(curthread, regs.SP()+uint64(scope.BinInfo.Arch.PtrSize()))

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Report to go-delve/delve including the original error text (embedded via %v) and the expression evaluated — the undo protocol hit an inconsistent state.
  2. Simplify the expression: split multi-call expressions into separate evaluations so a failure doesn't occur mid-injection-sequence.
  3. Re-evaluate in a fresh session/state; the target may be left in a partially-modified state after a failed undo.
  4. Update Delve and Go toolchain; injection undo bugs are toolchain-sensitive.
Defensive patterns

Strategy: try-catch

Validate before calling

// validate the whole expression compiles before running it
if _, err := evalop.Compile(lookup, expr, flags); err != nil {
    return err // fail fast before any injection starts
}

Try / catch

_, err := scope.EvalExpression(expr, cfg)
if err != nil && strings.Contains(err.Error(), "could not undo injected call") {
    // target state may be dirty; re-attach or restart before further work
    return fmt.Errorf("eval of %q left target in uncertain state: %w", expr, err)
}

Prevention

When it happens

Trigger: An error occurs mid-eval (bad expression after a call op, runtime error during injected call) while stack.fncalls is non-empty AND fncallPeek() == stack.lastRetiredFncall — i.e. the injection to undo was already retired, so undoing it would corrupt the target's state.

Common situations: Mixed expressions where a successful call injection is followed by a failing operation; errors during argument evaluation of a later call after an earlier one retired; recordings or attached sessions where undo breakpoints behave differently; Go runtime changes breaking injection undo semantics.

Related errors


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