{"record":{"id":"2e3a65bbfc254565","repo":"xpipe-io/xpipe","slug":"wrong-launch-context","errorCode":null,"errorMessage":"Wrong launch context","messagePattern":"Wrong launch context","errorType":"exception","errorClass":"BeaconClientException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/xpipe/app/terminal/TerminalLauncherManager.java","lineNumber":87,"sourceCode":"            return req.getResult() instanceof TerminalLaunchResult.ResultSuccess;\n        }\n    }\n\n    public static void registerPid(UUID request, long pid) throws BeaconClientException {\n        TerminalLaunchRequest req;\n        synchronized (entries) {\n            req = entries.get(request);\n        }\n        if (req == null) {\n            return;\n        }\n        var byPid = ProcessHandle.of(pid);\n        if (byPid.isEmpty()) {\n            throw new BeaconClientException(\"Unable to find terminal child process \" + pid);\n        }\n        var shell = byPid.get().parent().orElseThrow();\n        if (req.getShellPid() != -1 && shell.pid() != req.getShellPid()) {\n            throw new BeaconClientException(\"Wrong launch context\");\n        }\n        req.setShellPid(shell.pid());\n    }\n\n    public static void waitExchange(UUID request) throws BeaconServerException {\n        TerminalLaunchRequest req;\n        synchronized (entries) {\n            req = entries.get(request);\n        }\n        if (req == null) {\n            return;\n        }\n\n        if (req.isSetupCompleted()) {\n            submitAsync(req.getRequest(), req.getProcessControl(), req.getConfig(), req.getWorkingDirectory());\n        }\n        try {\n            req.waitForCompletion();","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/xpipe-io/xpipe/blob/d85ca821baa46092a320ebb13546d7240adb74f8/app/src/main/java/io/xpipe/app/terminal/TerminalLauncherManager.java#L69-L105","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: registering an arbitrary pid\nTerminalLauncherManager.registerPid(anyPid, req);\n// after: only register the pid recorded by the launch exchange\nvar script = TerminalLauncherManager.launchExchange(request);\nTerminalLauncherManager.registerPid(launchedPidFromThisScript, req);","handlingStrategy":"validation","validationCode":"var handle = ProcessHandle.of(pid);\nif (handle.isEmpty()) throw new IllegalStateException(\"pid not found\");\nlong parentPid = handle.get().parent().map(ProcessHandle::pid).orElse(-1L);\nif (req.getShellPid() != -1 && parentPid != req.getShellPid()) {\n    throw new IllegalStateException(\"pid \" + pid + \" parent \" + parentPid + \" != expected \" + req.getShellPid());\n}","typeGuard":null,"tryCatchPattern":"try {\n    TerminalLauncherManager.registerPid(pid, req);\n} catch (BeaconClientException e) {\n    logger.warn(\"launch context mismatch for pid {}: {}\", pid, e.getMessage());\n}","preventionTips":["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"],"tags":["terminal","process","launch","pid"],"backgroundTag":"internal-invariant-violation","analyzedSha":"d85ca821baa46092a320ebb13546d7240adb74f8","analyzedAt":"2026-09-06T14:30:08.251Z","contentChangedAt":"2026-09-06T14:30:08.251Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}