go-delve/delve · error

unknown goroutine %d

Error message

unknown goroutine %d

What it means

FindGoroutine resolves a goroutine ID to a *G using the partial and full goroutine caches plus runtime scans. Goroutine ID 0 is special: it is never a valid user goroutine, so requesting it explicitly (while not falling into the 'return selected' case) yields this error. The caller passed a goroutine identifier the debugger cannot resolve.

Source

Thrown at pkg/proc/variables.go:413

		//    to avoid reading any other goroutine and, more importantly, (b) we
		//    could be reading an incorrect value for the goroutine ID of a thread.
		//    This condition usually happens when a goroutine calls runtime.clone
		//    and for a short period of time two threads will appear to be running
		//    the same goroutine.
		// 3. if the caller asks for gid == 0 and the selected goroutine is
		//    either 0 or nil.
		//    Goroutine 0 is special, it either means we have no current goroutine
		//    (for example, running C code), or that we are running on a special
		//    stack (system stack, signal handling stack) and we didn't properly
		//    detect it.
		//    Since there could be multiple goroutines '0' running simultaneously
		//    if the user requests it return the one that's already selected or
		//    nil if there isn't a selected goroutine.
		return selg, nil
	}

	if gid == 0 {
		return nil, fmt.Errorf("unknown goroutine %d", gid)
	}

	if g := dbp.gcache.partialGCache[gid]; g != nil {
		return g, nil
	}

	// Calling GoroutinesInfo could be slow if there are many goroutines
	// running, check if a running goroutine has been requested first.
	for _, thread := range dbp.ThreadList() {
		g, _ := GetG(thread)
		if g != nil && g.ID == gid {
			return g, nil
		}
	}

	const goroutinesInfoLimit = 10
	nextg := 0
	for nextg >= 0 {

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Pass the actual target goroutine ID (positive integer) obtained from State()/ListGoroutines.
  2. Use GoroutineID = -1 (meaning 'use current/selected goroutine') instead of 0 for the non-specific case.
  3. Verify the goroutine still exists; re-fetch the goroutine list since IDs become invalid after exit.

Example fix

// before
scope, err := debugger.ConvertEvalScope(target, 0, frame, 0)

// after
gid := -1 // or a real goroutine ID from state.CurrentThread goroutine
scope, err := debugger.ConvertEvalScope(target, gid, frame, 0)
Defensive patterns

Strategy: validation

Validate before calling

if gid == 0 {
    gid = -1 // sentinel: use currently selected goroutine
}
if gid > 0 {
    found := false
    for _, g := range goroutineList { if g.ID == gid { found = true; break } }
    if !found { return fmt.Errorf("goroutine %d not in current list", gid) }
}

Try / catch

scope, err := client.ConvertEvalScope(pid, gid, frame, 0)
if err != nil && strings.Contains(err.Error(), "unknown goroutine") {
    state, _ := client.GetState()
    gid = state.SelectedGoroutine.ID
    scope, err = client.ConvertEvalScope(pid, gid, frame, 0)
}

Prevention

When it happens

Trigger: Calling debugger.ConvertEvalScope (or any API that reaches proc.FindGoroutine) with gid == 0, e.g. an RPC EvalScope/Command with GoroutineID=0 on a target whose API contract expects a positive goroutine ID.

Common situations: IDE/DAP clients sending GoroutineID 0 for 'current goroutine' when the server-side convention instead expects -1 (any) or the actual selected ID; scripts that default an unset variable to 0.

Related errors


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