Skip to content

fix(svelte): filter stale indices in Virtualizer to prevent undefined data access#847

Merged
inokawa merged 3 commits intoinokawa:mainfrom
jveres:fix/svelte-stale-indices
Feb 16, 2026
Merged

fix(svelte): filter stale indices in Virtualizer to prevent undefined data access#847
inokawa merged 3 commits intoinokawa:mainfrom
jveres:fix/svelte-stale-indices

Conversation

@jveres
Copy link
Contributor

@jveres jveres commented Jan 24, 2026

fix #851

Summary

  • Add data.length dependency to indexes derived to ensure recalculation when data changes
  • Filter indices to valid range (index >= 0 && index < len) to prevent stale indices

Problem

During reactive updates (e.g., delete operations), the indexes derived could contain stale indices that exceed the current data.length, causing "Cannot
read properties of undefined" errors when accessing data[index] in the #each block.

Solution

By accessing data.length in the indexes derived, we create a reactive dependency that ensures indexes recalculates atomically with data changes. The
filtering ensures only valid indices are included.

Test plan

  • Create structural changes (e.g. delete) in items of a virtualized list with Svelte 5
  • Verify no runtime exceptions occur

… data access

Add data.length dependency to indexes derived and filter indices to valid range.
This prevents stale indices from causing "Cannot read properties of undefined"
errors during reactive updates when data array length changes (e.g., delete operations).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@inokawa
Copy link
Owner

inokawa commented Jan 25, 2026

Hi, could you provide a reproducible code for the problem?

@jveres
Copy link
Contributor Author

jveres commented Jan 26, 2026

Here's a minimal reproduction you can paste into the Svelte playground:

<script>
    import { VList } from "virtua/svelte";

    let items = $state(
      Array.from({ length: 100 }, (_, i) => ({ id: i, text: `Item ${i}` }))
    );

    function deleteItem(id) {
      items = items.filter((item) => item.id !== id);
    }
</script>

<!--
  Steps to reproduce:
  1. Scroll down to the bottom of the list (items 95-99)
  2. Click "Delete" on any visible item
  3. BUG: "Cannot read properties of undefined" error

  The range still contains indices 95-99, but after deletion
  data.length is 99, so data[99] is undefined.
-->

<div style="height: 400px; overflow: auto;">
  <VList data={items} getKey={(item) => item.id}>
    {#snippet children(item, index)}
      <div style="height: 50px; padding: 8px; border-bottom: 1px solid #ccc; display: flex; justify-content: space-between; align-items: center;">
        <span>{item.text}</span>
        <button onclick={() => deleteItem(item.id)}>Delete</button>
      </div>
    {/snippet}
  </VList>
</div>

Steps to trigger the bug:

  1. Scroll to the very bottom of the list (so items 95-99 are visible)
  2. Click "Delete" on any item
  3. You'll see "Cannot read properties of undefined" because indexes contains stale indices that exceed the new data.length

@inokawa inokawa merged commit 14c0c39 into inokawa:main Feb 16, 2026
5 checks passed
@inokawa
Copy link
Owner

inokawa commented Feb 16, 2026

Shipped in 0.48.6. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Svelte getKey is called with undefined when data length decreases

2 participants