From 09e004fab303b2d225a548e60f3f428cd890e553 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Wed, 19 Aug 2026 22:03:35 -0700 Subject: [PATCH] Move PTY cleanup to localized error boundaries Restore provisional PTY size when build-options fails and guard pre-allocated handle registration. This ensures cleanup happens at the point of error, not deferred to the general catch block. --- src/main/ipc/pty/ipc/spawn-commit-persist.ts | 5 ++++- src/main/ipc/pty/ipc/spawn-commit.ts | 1 - src/main/ipc/pty/ipc/spawn-run.ts | 6 ++++-- src/main/ipc/pty/runtime/spawn.ts | 6 ++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/ipc/pty/ipc/spawn-commit-persist.ts b/src/main/ipc/pty/ipc/spawn-commit-persist.ts index 33b54fb8a6d..be9cb31394a 100644 --- a/src/main/ipc/pty/ipc/spawn-commit-persist.ts +++ b/src/main/ipc/pty/ipc/spawn-commit-persist.ts @@ -84,7 +84,10 @@ export async function persistPtyIpcSpawnCommit(ctx: PtyIpcSpawnState): Promise<{ }) } if (ctx.preAllocatedHandle && !ctx.stablePaneOwner?.handle) { - ctx.deps.runtime?.registerPreAllocatedHandleForPty(ctx.result.id, ctx.preAllocatedHandle) + if (ctx.deps.runtime?.registerPreAllocatedHandleForPty) { + ctx.deps.runtime.registerPreAllocatedHandleForPty(ctx.result.id, ctx.preAllocatedHandle) + ctx.agentTeamsLeaderHandle = null + } } ptySizes.set(ctx.result.id, { cols: args.cols, rows: args.rows }) if (ctx.effectiveSessionAppId !== undefined && ctx.effectiveSessionAppId !== ctx.result.id) { diff --git a/src/main/ipc/pty/ipc/spawn-commit.ts b/src/main/ipc/pty/ipc/spawn-commit.ts index 7711ba3c3e1..82a3aea1b74 100644 --- a/src/main/ipc/pty/ipc/spawn-commit.ts +++ b/src/main/ipc/pty/ipc/spawn-commit.ts @@ -25,7 +25,6 @@ import { persistPtyIpcSpawnCommit } from './spawn-commit-persist' export async function commitPtyIpcSpawn(ctx: PtyIpcSpawnState): Promise { const args = ctx.args const { rendererPreSignaled, rendererAlreadyRegistered } = await persistPtyIpcSpawnCommit(ctx) - ctx.agentTeamsLeaderHandle = null // Why: seed the headless emulator before registerPty so concurrent live PTY data lands on top of the seed, not replacing it (mobile keeps the daemon-restored scrollback). // Skip when the renderer will be authoritative — its xterm buffer is richer than the daemon snapshot. diff --git a/src/main/ipc/pty/ipc/spawn-run.ts b/src/main/ipc/pty/ipc/spawn-run.ts index 71f290a457f..748eb5d8f62 100644 --- a/src/main/ipc/pty/ipc/spawn-run.ts +++ b/src/main/ipc/pty/ipc/spawn-run.ts @@ -38,7 +38,10 @@ export async function runPtyIpcSpawn(deps: PtySpawnIpcDeps, args: PtySpawnIpcArg try { await preparePtyIpcSpawnPreflight(ctx) await assemblePtyIpcSpawnEnv(ctx) - const earlyReserved = await buildPtyIpcSpawnOptions(ctx) + const earlyReserved = await buildPtyIpcSpawnOptions(ctx).catch((error: unknown) => { + restoreProvisionalPtySize(ctx) + throw error + }) if (earlyReserved) { // Why: this request lost the pane to the reservation winner, so its // pre-allocated leader handle never binds to a PTY. Nothing else can @@ -51,7 +54,6 @@ export async function runPtyIpcSpawn(deps: PtySpawnIpcDeps, args: PtySpawnIpcArg return await commitPtyIpcSpawn(ctx) } catch (err) { releaseAbandonedAgentTeamsLeader(ctx) - restoreProvisionalPtySize(ctx) if (ctx.preSpawnHiddenMarkId !== null) { ctx.deps.transitionSpawnHiddenRendererPtyDeliveryState(ctx.preSpawnHiddenMarkId, false) } diff --git a/src/main/ipc/pty/runtime/spawn.ts b/src/main/ipc/pty/runtime/spawn.ts index 48768a31dc4..2af050cd796 100644 --- a/src/main/ipc/pty/runtime/spawn.ts +++ b/src/main/ipc/pty/runtime/spawn.ts @@ -87,14 +87,16 @@ export async function spawnPtyFromRuntimeController( if (earlyAdopt) { return toRuntimeSpawnReply(earlyAdopt) } - const earlyReserved = await buildRuntimePtySpawnOptions(ctx) + const earlyReserved = await buildRuntimePtySpawnOptions(ctx).catch((error: unknown) => { + restoreProvisionalPtySize(ctx) + throw error + }) if (earlyReserved) { return toRuntimeSpawnReply(earlyReserved) } await executeRuntimePtySpawn(ctx) return toRuntimeSpawnReply(await commitRuntimePtySpawn(ctx)) } catch (err) { - restoreProvisionalPtySize(ctx) if (ctx.pendingRegistrationPtyId) { deps.runtime?.cancelPendingPtyRegistration?.( ctx.pendingRegistrationPtyId,