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
- Verify the launch request is fresh and only used for one launch; create a new request UUID per launch
- Check that no wrapper script or daemonizing launcher inserts an extra process between the shell and the terminal child
- Ensure the shell that received the exec command is still the parent when the child spawns (avoid double-forking wrappers)
- 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
- Use a fresh launch request per launch
- Avoid wrapper scripts that fork intermediate processes between shell and terminal child
- Log parent PIDs when debugging launch flows
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
- Unable to find terminal child process ${pid}
- ex
- Invalid launch request state ${request}
- No connection found for input " + msg.getConnection()
- Multiple stores found: " + storeNames
AI-assisted analysis of xpipe-io/xpipe@d85ca821ba (2026-09-06).
Data as JSON: /api/errors/2e3a65bbfc254565.
Report an issue: GitHub.