From 9d6b64cabf28618f74b6868e9ca2cb8ffa5c8dec Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 14 Aug 2026 19:21:55 -0700 Subject: [PATCH] fix(gateway): release failed workspace upload claims (#123830) Reset claimed workspace uploads when HTTP validation fails before staging so a later reconciliation can mint a fresh operation. --- .../node-workspace-transfer-service.test.ts | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/gateway/worker-environments/node-workspace-transfer-service.test.ts b/src/gateway/worker-environments/node-workspace-transfer-service.test.ts index f2016c370d2e..8e68ae360740 100644 --- a/src/gateway/worker-environments/node-workspace-transfer-service.test.ts +++ b/src/gateway/worker-environments/node-workspace-transfer-service.test.ts @@ -251,13 +251,11 @@ describe("node workspace transfer service", () => { persistenceRetry.release(); } await upload; - const replay = await fetch( - `${httpOrigin}/__openclaw__/worker-transfer/v1/environments/environment-1/reconciliations/${prepared.snapshot.manifestRef.slice(7)}`, - { - method: "POST", - headers: { authorization: `Bearer ${uploadToken}`, "content-length": "0" }, - }, - ); + const reconciliationUrl = `${httpOrigin}/__openclaw__/worker-transfer/v1/environments/environment-1/reconciliations/${prepared.snapshot.manifestRef.slice(7)}`; + const replay = await fetch(reconciliationUrl, { + method: "POST", + headers: { authorization: `Bearer ${uploadToken}`, "content-length": "0" }, + }); expect(replay.status).toBe(404); const uploaded = service.takeUpload("environment-1", prepared.snapshot.manifestRef); expect(uploaded.current.entries).toContainEqual( @@ -266,6 +264,23 @@ describe("node workspace transfer service", () => { await expect( fs.readFile(path.join(uploaded.stagingRoot, "result.txt"), "utf8"), ).resolves.toBe("node result\n"); + const invalidUploadToken = service.prepareUpload( + "environment-1", + prepared.snapshot.manifestRef, + ); + const invalidUpload = await fetch(reconciliationUrl, { + method: "POST", + headers: { authorization: `Bearer ${invalidUploadToken}`, "content-length": "0" }, + }); + expect(invalidUpload.status).toBe(413); + await expect(invalidUpload.json()).resolves.toEqual({ + error: "workspace_transfer_limit", + }); + const replacementUploadToken = service.prepareUpload( + "environment-1", + prepared.snapshot.manifestRef, + ); + service.revoke("environment-1", replacementUploadToken); writeFaults.failNextWrite(new Error("injected terminal upload write failure")); const failedUploadToken = service.prepareUpload( "environment-1",