xpipe-io/xpipe · error · BeaconClientException

Wrong launch context

Error message

Wrong launch context

What it means

XPipe throws this BeaconClientException during terminal launch PID registration when the parent process of the freshly spawned terminal child does not match the shell PID recorded in the original launch request. It guards against registering a PID from a different launch context, i.e. the process tree changed between issuing the launch request and the child appearing. This prevents attaching a terminal session to the wrong shell.

Source

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

            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()) {
            submitAsync(req.getRequest(), req.getProcessControl(), req.getConfig(), req.getWorkingDirectory());
        }
        try {
            req.waitForCompletion();

View on GitHub (pinned to d85ca821ba)

Solutions

  1. Verify the launch request is fresh and only used for one launch; create a new request UUID per launch
  2. Check that no wrapper script or daemonizing launcher inserts an extra process between the shell and the terminal child
  3. Ensure the shell that received the exec command is still the parent when the child spawns (avoid double-forking wrappers)
  4. Re-run the launch; if flaky, log both shell.pid() and req.getShellPid() to identify which process is the actual parent

Example fix

// before: registering an arbitrary pid
TerminalLauncherManager.registerPid(anyPid, req);
// after: only register the pid recorded by the launch exchange
var script = TerminalLauncherManager.launchExchange(request);
TerminalLauncherManager.registerPid(launchedPidFromThisScript, req);
Defensive patterns

Strategy: validation

Validate before calling

var handle = ProcessHandle.of(pid);
if (handle.isEmpty()) throw new IllegalStateException("pid not found");
long parentPid = handle.get().parent().map(ProcessHandle::pid).orElse(-1L);
if (req.getShellPid() != -1 && parentPid != req.getShellPid()) {
    throw new IllegalStateException("pid " + pid + " parent " + parentPid + " != expected " + req.getShellPid());
}

Try / catch

try {
    TerminalLauncherManager.registerPid(pid, req);
} catch (BeaconClientException e) {
    logger.warn("launch context mismatch for pid {}: {}", pid, e.getMessage());
}

Prevention

When it happens

Trigger: Calling registerPid(pid) for a process whose immediate parent's PID differs from req.getShellPid() (when shellPid is not -1). Happens when the child was reparented, an intermediate wrapper process (e.g. script wrapper or daemonized launcher) spawned it, or the same request was used for an unrelated process.

Common situations: Terminal multiplexers or shell wrappers spawning the terminal as an intermediate process; the shell exited and the child was re-parented to init; race where the recorded shell died and PID reuse gave a different parent; calling registerPid manually with a PID obtained outside the launch flow.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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