From 73253f35b1078b73067912e441ad6ad5af7ff18e Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Fri, 23 Oct 2026 16:32:26 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20fix:=20Refuse=20a=20Pathname=20R?= =?UTF-8?q?oot=20That=20Changes=20Before=20It=20Is=20Held?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pathname-only root replaced between its identity capture and its open is refused as INVALID_PATH instead of rerunning the mutation unanchored against the replacement. --- packages/code/src/workspace.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/code/src/workspace.ts b/packages/code/src/workspace.ts index 3990214c..ec5a4218 100644 --- a/packages/code/src/workspace.ts +++ b/packages/code/src/workspace.ts @@ -809,8 +809,10 @@ async function hasMissingParent(root: string, path: string): Promise { * or `--environment`) under a descriptor held for just this request when its * parent is missing, so the mutation, parent creation included, takes the same * anchored path a selected project root does and a swapped symlink cannot - * redirect it outside the root. A held root, an existing parent, or a host that - * cannot anchor the root runs the mutation as before. + * redirect it outside the root. A held root or an existing parent runs the + * mutation as before; a root that is no longer a real directory at its path + * runs it unanchored and is refused there, creating nothing. A root that + * changes between its capture and its open is refused as `INVALID_PATH`. */ async function withAnchoredParentCreation( root: string, @@ -823,8 +825,13 @@ async function withAnchoredParentCreation( try { return await withWorkspaceRoot(root, identity, action); } catch (error) { - /** Raised only while opening the root, before `action` ran. */ - if (error instanceof WorkspaceRootAccessError) return action(); + /** + * Raised only while opening the root, before `action` ran: the root changed after its + * identity was captured. Never retry unanchored against whatever replaced it. + */ + if (error instanceof WorkspaceRootAccessError) { + throw new WorkspaceToolError('Invalid workspace path', 'INVALID_PATH'); + } throw error; } }