diff --git a/packages/plugins/sandbox-providers/daytona/src/plugin.test.ts b/packages/plugins/sandbox-providers/daytona/src/plugin.test.ts index 5380d120e2b..b3c24ebf224 100644 --- a/packages/plugins/sandbox-providers/daytona/src/plugin.test.ts +++ b/packages/plugins/sandbox-providers/daytona/src/plugin.test.ts @@ -558,6 +558,23 @@ describe("Daytona sandbox provider plugin", () => { expect(JSON.stringify(environmentCreationCleanupErrorData(error))).not.toContain(createError.message); }); + it("preserves ownership when the SDK adds labels to its mutable create input", async () => { + mockCreate.mockImplementation(async (input) => { + // Daytona 0.203.0 writes its default language into params.labels. + input.labels["code-toolbox-language"] = "python"; + throw createError; + }); + const { error, cleanup } = await unresolvedCreation(); + expect(environmentCreationCleanupErrorData(error)).toBeDefined(); + expect(cleanup.labels).not.toHaveProperty("code-toolbox-language"); + const orphan = ownedSandbox(); + mockGet.mockResolvedValue(orphan); + await expect(plugin.definition.onEnvironmentDestroyLease!({ ...params, + providerLeaseId: cleanup.providerLeaseId, leaseMetadata: { failedCreateCleanup: cleanup }, + })).resolves.toEqual({ providerLeaseId: cleanup.providerLeaseId, state: "destroyed" }); + expect(orphan.delete).toHaveBeenCalledWith(10, true); + }); + it("retries a late-visible failed creation using its persisted ownership envelope", async () => { const { cleanup } = await unresolvedCreation(); const orphan = ownedSandbox(); mockGet.mockResolvedValue(orphan); diff --git a/packages/plugins/sandbox-providers/daytona/src/plugin.ts b/packages/plugins/sandbox-providers/daytona/src/plugin.ts index b153ff935c5..4acbc2f9a01 100644 --- a/packages/plugins/sandbox-providers/daytona/src/plugin.ts +++ b/packages/plugins/sandbox-providers/daytona/src/plugin.ts @@ -955,7 +955,9 @@ async function createSandbox( purpose: options.purpose, reuseLease: config.reuseLease, }), "paperclip-create-attempt": attemptId }; - const createParams = { ...buildCreateParams(config, labels), name }; + // The SDK mutates params.labels (for example, code-toolbox-language). + // Preserve our immutable ownership snapshot for validation and retry. + const createParams = { ...buildCreateParams(config, { ...labels }), name }; try { return await client.create(createParams, { timeout: toTimeoutSeconds(config.timeoutMs),