go-delve/delve · error

no thread with id %d

Error message

no thread with id %d

What it means

GetThread throws this when the debugger has no OS thread with the requested numeric ID. FindThread(id) returned nil without error, meaning the ID is well-formed but no matching thread exists (or existed) in the target process.

Source

Thrown at service/rpc2/server.go:437

	return nil
}

type GetThreadIn struct {
	Id int
}

type GetThreadOut struct {
	Thread *api.Thread
}

// GetThread gets a thread by its ID.
func (s *RPCServer) GetThread(arg GetThreadIn, out *GetThreadOut) error {
	t, err := s.debugger.FindThread(arg.Id)
	if err != nil {
		return err
	}
	if t == nil {
		return fmt.Errorf("no thread with id %d", arg.Id)
	}
	_, unlock := s.debugger.LockTargetGroup()
	defer unlock()
	out.Thread = api.ConvertThread(t, s.debugger.ConvertThreadBreakpoint(t))
	return nil
}

type ListPackageVarsIn struct {
	Filter string
	Cfg    api.LoadConfig
}

type ListPackageVarsOut struct {
	Variables []api.Variable
}

// ListPackageVars lists all package variables in the context of the current thread.
func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsOut) error {

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Re-list threads with ListThreads immediately before GetThread and use a fresh ID
  2. Handle the error gracefully: the thread exiting between calls is normal in concurrent programs
  3. Use State/ goroutine APIs (Stacktrace on a goroutine) instead of raw thread IDs when possible, since goroutine IDs are more stable in delve workflows
  4. Verify the target is running/attached and threads exist before querying

Example fix

// before
thr, err := client.GetThread(rpc2.GetThreadIn{Id: tid}) // tid may be stale
// after
state, _ := client.State(rpc2.StateIn{})
for _, t := range state.Threads {
    if t.ID == tid {
        thr, err = client.GetThread(rpc2.GetThreadIn{Id: tid})
        break
    }
}
Defensive patterns

Strategy: validation

Validate before calling

func threadExists(client *rpc2.RPCClient, tid int) bool {
    st, err := client.State(rpc2.StateIn{})
    if err != nil { return false }
    for _, t := range st.Threads {
        if t.ID == tid { return true }
    }
    return false
}

Try / catch

thr, err := client.GetThread(rpc2.GetThreadIn{Id: tid})
if err != nil && strings.Contains(err.Error(), "no thread with id") {
    // thread exited — refresh and continue
    return nil
}

Prevention

When it happens

Trigger: Calling RPCClient.GetThread with a thread ID obtained from a previous stop event after the thread exited; an ID that never existed in this process; querying before the target has started, or after it exited, when the thread table is empty.

Common situations: Multithreaded programs where worker threads finish between a ListThreads call and a GetThread call; IDE thread panes caching thread IDs across continuations; attaching to a process after the interesting thread has terminated.

Related errors


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