Skip to content

Conversation

@apankov1
Copy link

@apankov1 apankov1 commented Nov 26, 2025

Summary

The @Persist proxy error handler was auto-vivifying {} on ANY error during property access, causing:

  1. Silent data corruption: Original property values replaced with empty objects
  2. Heap overflow: Infinite proxy recursion when the getter continues to throw

Root Cause

// Before: creates {} and recurses infinitely
} catch (e) {
    console.error(`Error accessing property ${String(key)}:`, e);
    const newObj = {};
    Reflect.set(target, key, newObj);
    return createDeepProxy(newObj, instance, propertyKey, triggerPersist);
}

If a getter throws, the error handler:

  1. Creates a new empty object
  2. Sets it on the target (corrupting the original)
  3. Returns a new proxy wrapping the empty object
  4. The next access triggers the same getter → same error → infinite loop

Fix

} catch (e) {
    console.error(`Error accessing property ${String(key)}:`, e);
    // Return undefined on error - don't auto-vivify
    return undefined;
}

Test Plan

  • Added test: throwing getter returns undefined, no heap overflow
  • Added test: original object not corrupted on error

Related


Code in this PR was AI-assisted and exercised against a pet project that experienced the bug.

The error handler was creating {} and returning a new proxy on ANY error,
which caused:
1. Silent data corruption (original value replaced with {})
2. Infinite proxy recursion leading to heap overflow

Fix: Return undefined on error instead of auto-vivifying.
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.

1 participant