go-delve/delve · error

could not read coroutine: %v

Error message

could not read coroutine: %v

What it means

When stepping into a Go coroutine callback (iter.Pull / range-over-func), Delve reads the captured 'c' variable (the runtime.coro) from the closure. If the resulting variable is marked Unreadable — its memory could not be read from the target — stepping into the coroutine fails with this error.

Source

Thrown at pkg/proc/target_exec.go:1783

	if err != nil {
		return false, fmt.Errorf("could not get registers trying to step into coroutine: %v", err)
	}
	dregs := bi.Arch.RegistersToDwarfRegisters(0, regs)
	cst := text[0].DestLoc.Fn.extra(bi).closureStructType
	clos := newVariable("", dregs.Uint64Val(bi.Arch.ContextRegNum), cst, p.BinInfo(), p.Memory())

	// Get variable 'c' from the current closure, change its type to
	// runtime.coro (it is normally iter.coro, which is an internal
	// placeholder).

	cvar, err := clos.structField("c")
	if err != nil {
		logflags.DebuggerLogger().Errorf("iter.Pull problems accessing captured 'c' variable in closure: %v", err)
		return false, nil
	}
	cvar = cvar.maybeDereference()
	if cvar.Unreadable != nil {
		return false, fmt.Errorf("could not read coroutine: %v", cvar.Unreadable)
	}
	typRuntimeCoro, err := bi.findType("runtime.coro")
	if err != nil {
		logflags.DebuggerLogger().Errorf("could not find runtime.coro type: %v", err)
		return false, nil
	}
	cvar = newVariable("", cvar.Addr, typRuntimeCoro, p.BinInfo(), p.Memory())

	// Set a breakpoint on the first user frame of goroutine c.gp (but
	// something special needs to happen if c.gp is executing
	// runtime.corostart).

	gp := cvar.loadFieldNamed("gp")
	if gp == nil {
		logflags.DebuggerLogger().Errorf("could not load runtime.coro.gp field (unreadable: %v)", cvar.Unreadable)
		return false, nil
	}
	gaddr, _ := constant.Uint64Val(gp.Value)

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Build the traced program without optimizations (-gcflags="all=-N -l") so the closure capture 'c' is materialized in memory
  2. Use a Go version and Delve version pair that both support coroutines (Go 1.23+ runtime.coro) — mismatched layouts make the read address invalid
  3. Update Delve to pick up fixes in closure/variable reading for coroutine stepping
  4. If the error is transient, retry the step; if persistent, step at the assembly level (step-instruction) instead

Example fix

// before: stepping into iter.Pull callback in an optimized binary
//   go build -o app .   // optimizations -> cvar unreadable
// after: disable optimizations for debuggable closures
//   go build -gcflags="all=-N -l" -o app .
Defensive patterns

Strategy: validation

Validate before calling

// before stepping into iterator callbacks, verify build flags and versions
// Go >= 1.23 required for runtime.coro
if !strings.HasPrefix(runtime.Version(), "go1.23") &&
   !strings.HasPrefix(runtime.Version(), "go1.2") {
    log.Fatal("coroutine stepping requires Go 1.23+")
}
// build with: go build -gcflags="all=-N -l" ...

Try / catch

err := doStepInto()
if err != nil && strings.Contains(err.Error(), "could not read coroutine") {
    // fall back to instruction-level stepping
    return doStepInstruction()
}

Prevention

When it happens

Trigger: stepIntoCoroutineMaybe dereferences the closure's captured variable 'c' and finds cvar.Unreadable != nil — the closure pointer was stale/zero, or the coroutine struct memory at the computed address is not readable in the target process.

Common situations: Stepping into iterator functions compiled with mismatched Go version vs Delve's expected runtime.coro layout; optimized builds where the closure capture was registerized/eliminated so the address is garbage; corrupted stack or wrong Context register value.

Related errors


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