Describe the bug
In the browser store build, createStore + produce can crash with a JS proxy invariant error when a getter returns a leaked produce draft proxy.
This reproduces in isolation with public APIs only. No raw state mutation, unwrap, or internal symbol access is needed.
Actual error:
TypeError: 'get' on proxy: property 'Symbol(solid-proxy)' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value
This does not reproduce with Node's default solid-js/store resolution, because that loads the server build. It does reproduce with the browser build.
Your Example Website or App
opencode e2e tests
Steps to Reproduce the Bug or Issue
- Create
repro.mjs with:
import { createStore, produce } from "solid-js/store"
let leaked
const [store, setStore] = createStore({
items: [],
get probe() {
return (this.items, leaked)
},
})
setStore(produce((draft) => {
leaked = draft.items
store.probe
}))
- Run:
node --conditions=browser repro.mjs
- Observe the proxy invariant error above.
If it helps, the same repro also works with plain node repro.mjs if the import is changed to:
import { createStore, produce } from "solid-js/store/dist/store.js"
Expected behavior
I would expect this to not hard-crash inside Solid.
Even if this is considered a sharp/unsupported edge case, I would expect one of these instead:
- the value is sanitized before it re-enters store machinery
- the getter result is safely ignored/rejected
- Solid throws a normal framework error instead of violating a JS proxy invariant
Screenshots or Videos
None.
Platform
- OS: Windows 11
- Browser: N/A; reproducing the browser store build via
node --conditions=browser
- Version: Node 24.14.1
solid-js: 1.9.10
Additional context
This appears to be specific to the browser store implementation.
From reading the built source, the likely path is:
wrap / store proxying attaches Symbol(solid-proxy) to the raw array
produce creates a draft proxy for that same raw array
unwrap() skips getter properties, so a getter can return the leaked draft proxy through a normal setStore(...) path
- a later read hits
setterTraps.get -> isWrappable(value), which reads value[$PROXY]
- that violates the proxy invariant because the target already has a non-configurable
Symbol(solid-proxy) property with a different value
Relevant locations in store/dist/store.js from solid-js@1.9.10:
isWrappable: line 31
- browser store proxy
get: line 103
unwrap: getter skip at line 49
setterTraps.get: line 399
produce: line 414
Describe the bug
In the browser store build,
createStore+producecan crash with a JS proxy invariant error when a getter returns a leakedproducedraft proxy.This reproduces in isolation with public APIs only. No raw state mutation,
unwrap, or internal symbol access is needed.Actual error:
This does not reproduce with Node's default
solid-js/storeresolution, because that loads the server build. It does reproduce with the browser build.Your Example Website or App
opencode e2e tests
Steps to Reproduce the Bug or Issue
repro.mjswith:If it helps, the same repro also works with plain
node repro.mjsif the import is changed to:Expected behavior
I would expect this to not hard-crash inside Solid.
Even if this is considered a sharp/unsupported edge case, I would expect one of these instead:
Screenshots or Videos
None.
Platform
node --conditions=browsersolid-js: 1.9.10Additional context
This appears to be specific to the browser store implementation.
From reading the built source, the likely path is:
wrap/ store proxying attachesSymbol(solid-proxy)to the raw arrayproducecreates a draft proxy for that same raw arrayunwrap()skips getter properties, so a getter can return the leaked draft proxy through a normalsetStore(...)pathsetterTraps.get -> isWrappable(value), which readsvalue[$PROXY]Symbol(solid-proxy)property with a different valueRelevant locations in
store/dist/store.jsfromsolid-js@1.9.10:isWrappable: line 31get: line 103unwrap: getter skip at line 49setterTraps.get: line 399produce: line 414