Re-file of archived genlayerlabs/genvm#329 (stranded read-only by the 2026-07-22 migration). Filed here per repo-split: this is executor/py-std domain.
⚠️ This needs an explicit team decision, not just a code change — the behavior is consensus-relevant, currently unpinned by any test, and diverges from EVM intuition. Whichever way the decision goes, a golden must lock it.
Behavior
When a contract runs a sandbox with allow_write_ops=True and the sandboxed callable fails, but the failure is caught by the parent, every effect the sandboxed code produced before failing — emitted events AND storage writes — persists into the parent execution.
Source: src/wasi/genlayer_sdk.rs, sandbox result handling — after rt::spawn_apply_run returns, the parent unconditionally adopts the sub-VM's state:
self.context.data.accumulator = my_res.vm_data.accumulator; // events ride the accumulator
self.context.data.storage = my_res.vm_data.storage;
No branch on the sub-VM's outcome, no rollback path. The adoption is symmetric (storage and events together), so the model is self-consistent — it just may not be what anyone consciously chose.
Why it needs a decision
- EVM divergence: a reverted sub-call's logs are dropped in EVM along with its state changes. Cross-ecosystem developers will carry the "caught revert ⇒ no logs" intuition into GenVM and be wrong. If persist-on-caught-failure is intended (the symmetric adopt suggests it is), it should be documented behavior in the python-sdk events docs, not incidental.
- Unpinned: no test covers emit-then-raise-in-sandbox; a refactor of the sandbox result path could silently flip the semantics either way.
Downstream verification: genlayer-node hit this building gen_getLogs e2e (NOD-322/324) and verified persist-on-caught-failure empirically on the v0.6.0-rc1 bundle (executor v0.3.0-rc7); their analysis concluded intended-by-architecture (their plan doc: .claude/plans/nod-322-gen-events-rpc.md §8 OQ10).
Proposed pinning test
tests/cases/stable/py/events/post_event_sandbox_caught.py: method that, inside an allow_write_ops=True sandbox, emits gl.chain.Event + writes storage, then raises; parent catches; method returns normally. Golden asserts the emission IS in the receipt and the storage write IS visible — pinning today's semantics. If the team instead chooses EVM-style rollback: same fixture flips to asserting absence, plus a changelog entry and major-version consideration (consensus-relevant change).
Ask
- Team decision: bless (document + pin) or change (rollback semantics + changelog + version bump).
- Pinning fixture per the decision (needs Linux CI to validate; I can write it once the decision lands).
Related: genvm-executor#4 (docstring re-port from the same downstream e2e work), genvm-manager#7 (LLM provider-exhaustion VmError, same requester ecosystem).
Behavior
When a contract runs a sandbox with
allow_write_ops=Trueand the sandboxed callable fails, but the failure is caught by the parent, every effect the sandboxed code produced before failing — emitted events AND storage writes — persists into the parent execution.Source:
src/wasi/genlayer_sdk.rs, sandbox result handling — afterrt::spawn_apply_runreturns, the parent unconditionally adopts the sub-VM's state:No branch on the sub-VM's outcome, no rollback path. The adoption is symmetric (storage and events together), so the model is self-consistent — it just may not be what anyone consciously chose.
Why it needs a decision
Downstream verification: genlayer-node hit this building
gen_getLogse2e (NOD-322/324) and verified persist-on-caught-failure empirically on the v0.6.0-rc1 bundle (executor v0.3.0-rc7); their analysis concluded intended-by-architecture (their plan doc:.claude/plans/nod-322-gen-events-rpc.md§8 OQ10).Proposed pinning test
tests/cases/stable/py/events/post_event_sandbox_caught.py: method that, inside anallow_write_ops=Truesandbox, emitsgl.chain.Event+ writes storage, then raises; parent catches; method returns normally. Golden asserts the emission IS in the receipt and the storage write IS visible — pinning today's semantics. If the team instead chooses EVM-style rollback: same fixture flips to asserting absence, plus a changelog entry and major-version consideration (consensus-relevant change).Ask
Related: genvm-executor#4 (docstring re-port from the same downstream e2e work), genvm-manager#7 (LLM provider-exhaustion VmError, same requester ecosystem).