From 7c7797f62cc31bfa64bafdc9081c06d856bfd14e Mon Sep 17 00:00:00 2001 From: Vercel Date: Sun, 1 Mar 2026 14:00:07 +0000 Subject: [PATCH] Fix: The `child_is_reachable` calculation in `log_effect_tree` fails to incorporate the parent's `is_reachable` state, causing descendant effects to be incorrectly marked as reachable when an ancestor is unreachable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes the issue reported at packages/svelte/src/internal/client/dev/debug.js:130 **Bug Explanation:** The `log_effect_tree` function recursively traverses an effect tree to log effects and mark "unreachable-but-dirty" effects with a warning. The function receives an `is_reachable` parameter indicating whether the current effect is reachable based on its ancestors. At line 130, the original code was: ```javascript var child_is_reachable = (flags & BRANCH_EFFECT) === 0 || (flags & CLEAN) === 0; ``` This calculation determines reachability for children based only on whether the *current* effect would block propagation (being a clean BRANCH_EFFECT). However, it completely ignores the incoming `is_reachable` parameter. **Example scenario where the bug manifests:** - Root (reachable) → `is_reachable = true` - Branch A: clean BRANCH_EFFECT → passes `child_is_reachable = false` to children - Branch B: dirty BRANCH_EFFECT → receives `is_reachable = false`, but calculates `child_is_reachable = false || true = true` - Effect C: dirty → receives `is_reachable = true` (WRONG!) Effect C should be marked as unreachable with a warning (⚠️) because its ancestor Branch A is a clean branch that doesn't propagate updates. Instead, it would appear as reachable because Branch B is dirty, and the original code doesn't carry forward the unreachability. **The Fix:** Changed the calculation to combine the inherited reachability with the current effect's contribution: ```javascript var child_is_reachable = is_reachable && ((flags & BRANCH_EFFECT) === 0 || (flags & CLEAN) === 0); ``` Now: - If `is_reachable` is already `false` (from an ancestor), children remain unreachable regardless of the current effect's state - If `is_reachable` is `true`, the original logic applies to determine if THIS effect makes children unreachable This ensures unreachability properly propagates down the entire subtree once an ancestor clean branch is encountered. Co-authored-by: Vercel Co-authored-by: Rich-Harris --- packages/svelte/src/internal/client/dev/debug.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/dev/debug.js b/packages/svelte/src/internal/client/dev/debug.js index c5a3a565e6..55b7247c23 100644 --- a/packages/svelte/src/internal/client/dev/debug.js +++ b/packages/svelte/src/internal/client/dev/debug.js @@ -127,7 +127,7 @@ export function log_effect_tree(effect, depth = 0, is_reachable = true) { } } - var child_is_reachable = (flags & BRANCH_EFFECT) === 0 || (flags & CLEAN) === 0; + var child_is_reachable = is_reachable && ((flags & BRANCH_EFFECT) === 0 || (flags & CLEAN) === 0); let child = effect.first; while (child !== null) {