go-delve/delve · error

could not continue new thread %d %s

Error message

could not continue new thread %d %s

What it means

When trapWaitInternal sees a new thread (LWP) on FreeBSD it adds it to the thread map and issues ptrace(PT_CONTINUE) with signal 0 to let it run. If that continue fails with anything other than ESRCH (the tolerated 'thread died meanwhile' case), delve wraps the tid and error. The debugger cannot resume the newly created thread.

Source

Thrown at pkg/proc/native/proc_freebsd.go:405

			} else if pl_flags&_PL_FLAG_BORN != 0 {
				th, err = dbp.addThread(int(tid), false)
				if err != nil {
					if err == sys.ESRCH {
						// process died while we were adding it
						continue
					}
					return nil, err
				}
				if mode == trapWaitStepping {
					dbp.execPtraceFunc(func() { ptraceSuspend(tid) })
				}
				if err = dbp.ptraceCont(0); err != nil {
					if err == sys.ESRCH {
						// thread died while we were adding it
						delete(dbp.threads, int(tid))
						continue
					}
					return nil, fmt.Errorf("could not continue new thread %d %s", tid, err)
				}
				continue
			}
		}

		if th == nil {
			continue
		}

		if mode == trapWaitStepping {
			return th, nil
		}
		if status.StopSignal() == sys.SIGTRAP || status.Continued() {
			// Continued in this case means we received the SIGSTOP signal
			return th, nil
		}

		// TODO(dp) alert user about unexpected signals here.

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Run the debugger with sufficient privileges (same user or root) so PT_CONTINUE on every LWP is permitted.
  2. Retry the operation; if the tid died (ESRCH) delve already tolerates it, other errno values may be transient after attach races.
  3. Check the underlying errno in the message; EPERM points at credentials/ptrace-scope, EINVAL at kernel state.
  4. Upgrade FreeBSD/delve if reproducible on valid tids.

Example fix

// before
if err := dbp.Continue(); err != nil { log.Fatal(err) } // could not continue new thread 4242 EPERM

// after
if err := dbp.Continue(); err != nil {
    if strings.Contains(err.Error(), "EPERM") {
        log.Fatal("delve lacks ptrace permission on target threads; run as same user or root")
    }
    log.Fatal(err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// check ptrace scope/credentials before debugging on FreeBSD
func checkPrivs() error {
    if os.Geteuid() == 0 { return nil }
    return fmt.Errorf("run as root or target owner for reliable thread continues")
}

Try / catch

err := dbp.Continue()
if err != nil && strings.Contains(err.Error(), "could not continue new thread") {
    if strings.Contains(err.Error(), "EPERM") {
        log.Fatal("insufficient ptrace privileges for new threads; run as root/same user")
    }
    // otherwise retry once
    err = dbp.Continue()
}

Prevention

When it happens

Trigger: ptraceCont(0) on a just-discovered tid returns an error other than ESRCH — e.g. EPERM because the tracer lost permission, or EINVAL from a bad state — while handling a new-thread stop event.

Common situations: Attaching to processes that spawn threads very rapidly; permission changes (target setuid, container dropping ptrace scope); FreeBSD version-specific PT_CONTINUE quirks.

Related errors


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