go-delve/delve · error

bad size returned by _VirtualQueryEx: %d (expected %d)

Error message

bad size returned by _VirtualQueryEx: %d (expected %d)

What it means

On Windows, MemoryMap walks the address space with _VirtualQueryEx and expects it to return exactly sizeof(MEMORY_BASIC_INFORMATION) bytes. A different size means the structure layout doesn't match what the OS returned (ABI/struct mismatch), so the memory map is invalid and the error is raised.

Source

Thrown at pkg/proc/native/dump_windows_amd64.go:40

		}

		maxaddr := uint64(1 << 48) // windows64 uses only 48 bit addresses
		if !is64 {
			maxaddr = uint64(^uint32(0))
		}

		var meminfo _MEMORY_BASIC_INFORMATION

		for addr := uint64(0); addr < maxaddr; addr += meminfo.RegionSize {
			size := _VirtualQueryEx(p.os.hProcess, uintptr(addr), &meminfo, unsafe.Sizeof(meminfo))
			if size == 0 {
				// size == 0 is an error and the only error returned by VirtualQueryEx
				// is when addr is above the highest address allocated for the
				// application.
				return
			}
			if size != unsafe.Sizeof(meminfo) {
				memoryMapError = fmt.Errorf("bad size returned by _VirtualQueryEx: %d (expected %d)", size, unsafe.Sizeof(meminfo))
				return
			}
			if addr+meminfo.RegionSize <= addr {
				// this shouldn't happen
				memoryMapError = errors.New("VirtualQueryEx wrapped around the address space or stuck")
				return
			}
			if meminfo.State == _MEM_FREE || meminfo.State == _MEM_RESERVE {
				continue
			}
			if meminfo.Protect&_PAGE_GUARD != 0 {
				// reading from this range will result in an error.
				continue
			}

			var mme proc.MemoryMapEntry
			mme.Addr = addr
			mme.Size = meminfo.RegionSize

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Build/run delve with matching GOARCH and target process bitness (amd64 binary for amd64 processes; handle WOW64 explicitly).
  2. Update Go/Delve so the MEMORY_BASIC_INFORMATION definition matches the current Windows ABI.
  3. Check windows build tags in pkg/proc/native (dump_windows_amd64.go vs other arch files) and use the correct binary.
  4. If the process is under WOW64, use a 32-bit delve or expect limited support.

Example fix

// before: 32-bit build inspecting a 64-bit process
set GOARCH=386 & go build ./cmd/dlv
dlv attach <amd64-pid>
// after
set GOARCH=amd64 & go build ./cmd/dlv
dlv attach <amd64-pid>
Defensive patterns

Strategy: validation

Validate before calling

// Verify bitness match before attaching/dumping on Windows
is64, _ := isWow64Process(hProcess)
if (runtime.GOARCH == "386") && !is64NotRequired { /* amd64 process needs amd64 debugger */ }
if runtime.GOARCH == "386" && is64BitProcess(pid) { return errors.New("use amd64 delve for 64-bit processes") }

Type guard

func structSizeMatches(size, expected uintptr) bool { return size == expected } // call with unsafe.Sizeof(meminfo) right after VirtualQueryEx

Try / catch

if err != nil && strings.Contains(err.Error(), "bad size returned by _VirtualQueryEx") {
    return fmt.Errorf("%w (check debugger GOARCH vs process bitness and Go/Windows SDK version)", err)
}

Prevention

When it happens

Trigger: Dumping a process memory map on Windows when VirtualQueryEx returns a size differing from unsafe.Sizeof(meminfo) — usually a wrong struct definition/build-tag mismatch (e.g. 32-bit vs 64-bit structures, wrong GOARCH build), or an unexpected OS version with different layout.

Common situations: Cross-compiled delve binaries (e.g. 386 build querying a WOW64 process) where MEMORY_BASIC_INFORMATION sizes differ; Windows SDK/kernel differences; corrupted syscall return.

Related errors


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