go-delve/delve · error

could not determine executable path: %v

Error message

could not determine executable path: %v

What it means

During gdbserial connection initialization, Delve must learn the inferior's executable path when the user did not supply one (attach mode). It first tries qXfer:exec-file:read (gdb way), then qProcessInfo (lldb way); if a non-'unsupported' protocol error comes back from readExecFile, the connection is closed and this error is thrown wrapping the underlying cause.

Source

Thrown at pkg/proc/gdbserial/gdbserver.go:719

func (p *gdbProcess) initialize(path, cmdline string, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
	var err error
	if path == "" {
		// If we are attaching to a running process and the user didn't specify
		// the executable file manually we must ask the stub for it.
		// We support both qXfer:exec-file:read:: (the gdb way) and calling
		// qProcessInfo (the lldb way).
		// Unfortunately debugserver on macOS supports neither.
		path, err = p.conn.readExecFile()
		if err != nil {
			if isProtocolErrorUnsupported(err) {
				_, path, err = queryProcessInfo(p, p.Pid())
				if err != nil {
					p.conn.conn.Close()
					return nil, err
				}
			} else {
				p.conn.conn.Close()
				return nil, fmt.Errorf("could not determine executable path: %v", err)
			}
		}
	}

	if path == "" {
		// try using jGetLoadedDynamicLibrariesInfos which is the only way to do
		// this supported on debugserver (but only on macOS >= 12.10)
		images, _ := p.conn.getLoadedDynamicLibraries()
		for _, image := range images {
			if image.MachHeader.FileType == macho.TypeExec {
				path = image.Pathname
				break
			}
		}
	}

	err = p.updateThreadList(&threadUpdater{p: p}, nil)
	if err != nil {

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Pass the executable path explicitly when attaching (dlv attach <pid> with the binary, or the gdbserial API's path argument) so readExecFile is never called
  2. Check the wrapped '%v' cause to see the actual protocol error and fix the stub/connection
  3. Use a known-good stub (recent gdbserver, lldb-server, or macOS debugserver) that supports qXfer:exec-file:read or qProcessInfo
  4. Verify the connection is stable (no proxy/firewall mangling packets) since transport errors surface here

Example fix

// before
target, err := client.Connect(pid, "") // empty path forces exec-file query
// after
target, err := client.Connect(pid, "/path/to/inferior") // path supplied, no exec-file lookup
Defensive patterns

Strategy: validation

Validate before calling

if path == "" {
    if fi, err := os.Stat(fmt.Sprintf("/proc/%d/exe", pid)); err == nil {
        path, _ = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
    }
}
// pass resolved path to Connect so readExecFile is skipped

Try / catch

tgt, err := client.Connect(pid, path)
if err != nil && strings.Contains(err.Error(), "could not determine executable path") {
    // fall back: supply the path explicitly and reconnect
    tgt, err = client.Connect(pid, resolvedPath)
}

Prevention

When it happens

Trigger: Connecting to a stub (gdbserver/debugserver/lldb-server/rr) in attach mode with an empty executable path where the qXfer:exec-file:read packet fails with a real protocol error (not a plain 'unsupported' reply) — e.g. malformed stub reply, connection-level failure, or a stub that returns an error other than the empty unsupported response.

Common situations: Attaching to a process through a nonstandard or buggy GDB remote stub; a stub that replies to qXfer:exec-file with an error string instead of the expected empty/unsupported packet; custom or older debugserver builds; networked gdbserver connections that corrupt replies.

Related errors


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