go-delve/delve · error

failed to get xstate: %v

Error message

failed to get xstate: %v

What it means

ptraceGetXsave issues PT_GETXSTATE via cgo ptrace(2) on FreeBSD/amd64 to read the thread's extended XSAVE area (XMM/YMM state). When the syscall returns non-zero, the raw errno is wrapped in this message. Note the message prints err, which is often nil after a failing cgo ptrace call, so the %v may render as an unhelpful value.

Source

Thrown at pkg/proc/native/ptrace_freebsd_amd64.go:53

	if xsaveLen > 0 {
		return xsaveLen, nil
	}
	if xsaveLen < 0 {
		return xsaveLen, fmt.Errorf("failed to get xstate info: %v", xsaveErr)
	}
	return ptraceGetXsaveLen(tid)
}

// ptraceGetXsave gets the X86 XSAVE data for the given tid.
func ptraceGetXsave(tid int) ([]byte, error) {
	len, err := ptraceXsaveLen(tid)
	if err != nil {
		return nil, err
	}
	xsaveBuf := make([]byte, len)
	ret, err := C.ptrace(C.PT_GETXSTATE, C.pid_t(tid), C.caddr_t(unsafe.Pointer(&xsaveBuf[0])), C.int(len))
	if ret != 0 {
		return nil, fmt.Errorf("failed to get xstate: %v", err)
	}
	return xsaveBuf, nil
}

// ptraceSetXsave sets the X86 XSAVE data for the given tid.
func ptraceSetXsave(tid int, xsaveBuf []byte) error {
	ret, err := C.ptrace(C.PT_SETXSTATE, C.pid_t(tid), C.caddr_t(unsafe.Pointer(&xsaveBuf[0])), C.int(len(xsaveBuf)))
	if ret != 0 {
		return fmt.Errorf("failed to set xstate: %v", err)
	}
	return nil
}

func ptraceGetRegset(id int) (*amd64util.AMD64Xstate, error) {
	var regset amd64util.AMD64Xstate
	ret, err := C.ptrace(C.PT_GETFPREGS, C.pid_t(id), C.caddr_t(unsafe.Pointer(&regset.AMD64PtraceFpRegs)), C.int(0))
	if ret != 0 {
		return nil, fmt.Errorf("failed to get FP registers: %v", err)

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Verify the target thread still exists (check thread ID validity / process alive) before reading registers
  2. Run on a FreeBSD version and CPU that support PT_GETXSTATE (XSAVE-capable CPU, FreeBSD with xstate support)
  3. Check ptrace permission (kern.securelevel, procfs/ptrace scope) and that delve runs as the same user or root
  4. If the errno shown is nil/empty, patch the wrapper to capture errno properly (e.g. via C.errno or runtime.SetFinalizer-free cgo errno handling) to diagnose

Example fix

// before
ret, err := C.ptrace(C.PT_GETXSTATE, C.pid_t(tid), C.caddr_t(unsafe.Pointer(&xsaveBuf[0])), C.int(len))
if ret != 0 {
	return nil, fmt.Errorf("failed to get xstate: %v", err)
}
// after
ret, err := C.ptrace(C.PT_GETXSTATE, C.pid_t(tid), C.caddr_t(unsafe.Pointer(&xsaveBuf[0])), C.int(len))
if ret != 0 {
	if err == nil {
		return nil, fmt.Errorf("failed to get xstate (PT_GETXSTATE), tid=%d, ret=%d", tid, ret)
	}
	return nil, fmt.Errorf("failed to get xstate: %v", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before reading registers
if !isThreadAlive(dbp, tid) {
	return fmt.Errorf("thread %d no longer exists; cannot read xstate", tid)
}
// optionally probe support once:
// _, err := ptraceGetXsave(tid) // cache whether PT_GETXSTATE works at attach time

Type guard

func regReadErr(err error) bool {
	return err != nil && strings.Contains(err.Error(), "failed to get xstate")
}

Try / catch

regs, err := thread.Registers()
if regReadErr(err) {
	// fall back to GPR-only path or mark thread dead
	return nil, err
}

Prevention

When it happens

Trigger: Any call chain that reads full registers of a FreeBSD amd64 thread (ptraceGetRegset -> ptraceGetXsave) when PT_GETXSTATE fails: the thread exited between attach and the call, the tid is invalid, or the kernel/CPU does not support the XSTATE ptrace interface (old FreeBSD or CPU lacking XSAVE).

Common situations: Debugging on FreeBSD <9/old kernels without PT_GETXSTATE; running on VMs/CPUs with XSAVE disabled; thread died mid-debug-session so tid is stale; permissions dropped so ptrace is denied.

Related errors


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