From 7a1c6ceaef54526fa8f765991bab6900c7a6d7e4 Mon Sep 17 00:00:00 2001 From: Ousama Ben Younes Date: Sat, 6 Jun 2026 08:04:55 +0000 Subject: [PATCH] fix(svelte-query): handle shrinking createRawRef array by multiple entries (#10341) The `createRawRef` Proxy used by `createQueries` crashed with `TypeError: 'deleteProperty' on proxy: trap returned falsish for property 'N'` whenever the reactive query list was shrunk by two or more items in one update. Two compounding causes: 1. `update()` iterated `keysToRemove` in ascending index order. The trap decrements `target.length` after every deletion, so the next stale index was no longer `in target` and the trap returned `false`, which throws in strict mode (ES modules). 2. The trap returned `false` for missing props instead of treating delete on a nonexistent prop as a spec-compliant no-op. Fix: sort `keysToRemove` descending for arrays so each delete acts on the current tail, and return `true` from `deleteProperty` when the prop is already absent. Generated by Ora Studio Vibe coded by ousamabenyounes Co-Authored-By: Claude --- .changeset/fix-svelte-query-shrink-array.md | 5 +++++ packages/svelte-query/src/containers.svelte.ts | 12 +++++++++++- .../tests/containers.svelte.test.ts | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-svelte-query-shrink-array.md diff --git a/.changeset/fix-svelte-query-shrink-array.md b/.changeset/fix-svelte-query-shrink-array.md new file mode 100644 index 00000000000..ee6ca8ff28f --- /dev/null +++ b/.changeset/fix-svelte-query-shrink-array.md @@ -0,0 +1,5 @@ +--- +'@tanstack/svelte-query': patch +--- + +Fix `createQueries` crashing with `TypeError: 'deleteProperty' on proxy: trap returned falsish for property 'N'` when two or more items were removed from its reactive array in the same update. diff --git a/packages/svelte-query/src/containers.svelte.ts b/packages/svelte-query/src/containers.svelte.ts index 60d27c68431..379fa0f2bd0 100644 --- a/packages/svelte-query/src/containers.svelte.ts +++ b/packages/svelte-query/src/containers.svelte.ts @@ -80,7 +80,10 @@ export function createRawRef>( } return true } - return false + // Spec semantics: deleting a missing prop is a no-op. Returning false here + // makes `delete` throw in strict mode, which broke #10341 when `update()` + // iterated stale indices over an already-shrunk array. + return true }, }) @@ -88,6 +91,13 @@ export function createRawRef>( const existingKeys = Object.keys(out) const newKeys = Object.keys(newValue) const keysToRemove = existingKeys.filter((key) => !newKeys.includes(key)) + // Arrays: delete in descending index order so each `deleteProperty` trap + // sees the slot it is removing as the current tail (length-- stays valid). + // Forward iteration would shrink the array under our feet and the next + // index would no longer be `in target`, tripping the trap. + if (Array.isArray(newValue)) { + keysToRemove.sort((a, b) => Number(b) - Number(a)) + } for (const key of keysToRemove) { // @ts-expect-error delete out[key] diff --git a/packages/svelte-query/tests/containers.svelte.test.ts b/packages/svelte-query/tests/containers.svelte.test.ts index 3511dbb5b5d..849dc2849c0 100644 --- a/packages/svelte-query/tests/containers.svelte.test.ts +++ b/packages/svelte-query/tests/containers.svelte.test.ts @@ -198,6 +198,24 @@ describe('createRawRef', () => { expect(ref).toEqual([7, 8, 9]) }) + it('should handle shrinking an array by more than one entry at once', () => { + // Regression for #10341: createQueries crashed with + // `TypeError: can't delete property 'N': proxy deleteProperty handler returned false` + // when two or more items were removed from the reactive array in a single update. + const [ref, update] = createRawRef([1, 2, 3, 4, 5]) + + expect(ref).toEqual([1, 2, 3, 4, 5]) + + update([1, 2]) + expect(ref).toEqual([1, 2]) + + update([1, 2, 3, 4]) + expect(ref).toEqual([1, 2, 3, 4]) + + update([]) + expect(ref).toEqual([]) + }) + it('should behave like a regular object when not using `update`', () => { const [ref] = createRawRef>({ a: 1, b: 2 })