go-delve/delve · error

NtQueryInformationThread failed: it returns 0x%x

Error message

NtQueryInformationThread failed: it returns 0x%x

What it means

Delve's native Windows backend calls NtQueryInformationThread with ThreadBasicInformation to obtain the thread's TEB base address when reading CPU registers. If the NT syscall returns a non-success NTSTATUS, the thread context cannot be assembled and an AMD64Registers object cannot be created. This typically means the thread handle is invalid, the thread has exited, or access rights are insufficient.

Source

Thrown at pkg/proc/native/threads_windows_amd64.go:29

)

func newContext() *winutil.AMD64CONTEXT {
	return winutil.NewAMD64CONTEXT()
}

func registers(t *nativeThread) (proc.Registers, error) {
	context := newContext()

	context.SetFlags(_CONTEXT_ALL)
	err := t.getContext(context)
	if err != nil {
		return nil, err
	}

	var threadInfo _THREAD_BASIC_INFORMATION
	status := _NtQueryInformationThread(t.os.hThread, _ThreadBasicInformation, &threadInfo, uint32(unsafe.Sizeof(threadInfo)), nil)
	if !_NT_SUCCESS(status) {
		return nil, fmt.Errorf("NtQueryInformationThread failed: it returns 0x%x", status)
	}

	return winutil.NewAMD64Registers(context, uint64(threadInfo.TebBaseAddress)), nil
}

func (t *nativeThread) setContext(context *winutil.AMD64CONTEXT) error {
	return _SetThreadContext(t.os.hThread, context)
}

func (t *nativeThread) getContext(context *winutil.AMD64CONTEXT) error {
	return _GetThreadContext(t.os.hThread, context)
}

func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error {
	return t.setContext(savedRegs.(*winutil.AMD64Registers).Context)
}

func (t *nativeThread) withDebugRegisters(f func(*amd64util.DebugRegisters) error) error {

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Retry the operation; transient races with thread exit usually resolve on a fresh stop state
  2. Re-list threads and only read registers for threads still in the target's thread list
  3. Run the debugger elevated (Administrator) to ensure sufficient handle access rights
  4. Check for security software interfering with debugger handle permissions

Example fix

// before
regs, err := thread.Registers()
// after
if !tg.HasThread(thread.ThreadID()) {
    return nil // thread exited, skip
}
regs, err := thread.Registers()
if err != nil {
    if isDeadThread(err) { return nil }
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

if tg.HasThread(thread.ThreadID()) { /* safe to read registers */ }

Type guard

func threadAlive(t proc.Thread, tg *proc.TargetGroup) bool { return tg.HasThread(t.ThreadID()) }

Try / catch

regs, err := thread.Registers(); if err != nil { if strings.Contains(err.Error(), "NtQueryInformationThread") { /* thread likely exited; refresh thread list and retry once */ } else { return err } }

Prevention

When it happens

Trigger: Calling Registers() on a native Windows thread whose OS thread has exited between stop and register read, or whose hThread handle was opened without THREAD_QUERY_INFORMATION rights.

Common situations: Debugging a rapidly exiting thread (race between thread death and register read); attaching to a process with restricted privileges; antivirus/security software blocking handle access; reading registers of a dead thread in a multithreaded program.

Related errors


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