xpipe-io/xpipe · error · BeaconClientException

Unable to find terminal child process ${pid}

Error message

Unable to find terminal child process ${pid}

What it means

TerminalLauncherManager.registerPid() links a spawned terminal child process (by PID) back to its launch request. If ProcessHandle.of(pid) finds no live process with that PID, it throws BeaconClientException("Unable to find terminal child process <pid>"). This validates the reported child PID exists before binding its parent shell PID to the pending request.

Source

Thrown at app/src/main/java/io/xpipe/app/terminal/TerminalLauncherManager.java:83

    @SuppressWarnings("unused")
    public static boolean isCompletedSuccessfully(UUID request) {
        synchronized (entries) {
            var req = entries.get(request);
            return req.getResult() instanceof TerminalLaunchResult.ResultSuccess;
        }
    }

    public static void registerPid(UUID request, long pid) throws BeaconClientException {
        TerminalLaunchRequest req;
        synchronized (entries) {
            req = entries.get(request);
        }
        if (req == null) {
            return;
        }
        var byPid = ProcessHandle.of(pid);
        if (byPid.isEmpty()) {
            throw new BeaconClientException("Unable to find terminal child process " + pid);
        }
        var shell = byPid.get().parent().orElseThrow();
        if (req.getShellPid() != -1 && shell.pid() != req.getShellPid()) {
            throw new BeaconClientException("Wrong launch context");
        }
        req.setShellPid(shell.pid());
    }

    public static void waitExchange(UUID request) throws BeaconServerException {
        TerminalLaunchRequest req;
        synchronized (entries) {
            req = entries.get(request);
        }
        if (req == null) {
            return;
        }

        if (req.isSetupCompleted()) {

View on GitHub (pinned to d85ca821ba)

Solutions

  1. Retry the launch, keeping the terminal open so the child process is alive when the PID is registered
  2. Verify the PID is reported correctly to registerPid (check env/argument passing of the pid)
  3. Check PID namespace visibility (container/SSH) and register from within the same namespace
  4. Log ProcessHandle.allProcesses() near failure time to confirm whether the child ever existed

Example fix

// before
long pid = readPidFile(); // stale pid
TerminalLauncherManager.registerPid(pid); // throws
// after
ProcessHandle.of(pid).ifPresentOrElse(
    h -> TerminalLauncherManager.registerPid(pid),
    () -> log.warn("pid " + pid + " already gone, relaunching"));
Defensive patterns

Strategy: try-catch

Validate before calling

if (ProcessHandle.of(pid).isEmpty()) {
    // child already gone; relaunch instead of registering
}

Type guard

Optional<ProcessHandle> handle = ProcessHandle.of(pid);
if (handle.isPresent() && handle.get().isAlive()) { /* safe to register */ }

Try / catch

try {
    TerminalLauncherManager.registerPid(pid);
} catch (BeaconClientException e) {
    if (e.getMessage().contains("Unable to find terminal child process")) {
        // child exited or PID invisible; relaunch the terminal
    }
}

Prevention

When it happens

Trigger: registerPid() called with a PID that is not a live process: child already exited and was reaped, PID reported by the terminal is wrong, or OS-level PID namespace differences (containers/SSH) make the PID invisible to this JVM.

Common situations: Terminal closed immediately after spawn so the process no longer exists; pidfile/env-reported PID stale by the time of registration; running inside a container where the child PID is not visible; platform differences in how the terminal reports its own PID.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of xpipe-io/xpipe@d85ca821ba (2026-09-06). Data as JSON: /api/errors/ad0ef34b626fac0e. Report an issue: GitHub.