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
- Retry the launch, keeping the terminal open so the child process is alive when the PID is registered
- Verify the PID is reported correctly to registerPid (check env/argument passing of the pid)
- Check PID namespace visibility (container/SSH) and register from within the same namespace
- 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
- Keep the terminal open until registration completes
- Pass the PID to the child reliably (env var/argument) and read it promptly
- Be aware of PID namespaces in containers/SSH when validating PIDs
- Check ProcessHandle.of(pid).isAlive() before calling registerPid
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
- Wrong launch context
- failure.getThrowable()
- Unknown launch request
- No active shell session known for id " + uuid
- Couldn't send request
AI-assisted analysis of xpipe-io/xpipe@d85ca821ba (2026-09-06).
Data as JSON: /api/errors/ad0ef34b626fac0e.
Report an issue: GitHub.