Skip to content

[Core] Fix crash from unsynchronized state delete race - #1850

Merged
r0x0r merged 2 commits into
masterfrom
fix/state-delete-race-v2
Oct 19, 2026
Merged

r0x0r merged 2 commits into
masterfrom
fix/state-delete-race-v2

Conversation

@r0x0r

@r0x0r r0x0r commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Problem

webview/js/state.js's Proxy set/delete traps dispatch the local change
CustomEvent before the pywebviewStateUpdate/pywebviewStateDelete bridge
call reaches Python. If a rapid set/delete race on the same state key ever
produces a stale or duplicate pywebviewStateDelete after the key was already
removed on the Python side, State.__delattr__/__delitem__ raised
AttributeError/KeyError.

js_bridge_call handled the state branches directly with no try/except
(unlike the normal js_api call path, which already has one), so the
exception propagated uncaught into the native bridge callback. On
Cocoa/PyObjC this surfaces as an uncaught ObjC exception (OC_PythonException)
that terminates the whole native process instead of just failing one call.

Fix

  • webview/util.py: wrap the pywebviewStateUpdate/pywebviewStateDelete
    handling in js_bridge_call in try/except so a stale or duplicate bridge
    message is logged instead of crashing the app.
  • webview/state.py: use a private _MISSING sentinel instead of None to
    represent "key not found" in _delete_state_value. This also fixes a
    related bug where deleting a key whose value is legitimately None
    incorrectly raised.

Testing

  • pytest tests/test_state.py — 20 passed
  • ruff check / ruff format --check — clean

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

webview/js/state.js dispatches the local 'change' CustomEvent *before*
sending the pywebviewStateUpdate bridge call to Python. If a duplicate or
stale pywebviewStateDelete for the same key ever arrives after the key was
already removed on the Python side (e.g. a rapid set/delete race from
JavaScript), State.__delattr__ raised AttributeError. js_bridge_call ran the
state update/delete branches directly without the try/except used for
regular js_api calls, so the exception propagated uncaught into the native
bridge callback. On Cocoa/PyObjC this surfaces as an uncaught ObjC exception
that kills the whole process instead of just failing one call.

Fixes:
- webview/util.py: wrap pywebviewStateUpdate/pywebviewStateDelete handling
  in try/except so a stale or duplicate bridge message logs an error instead
  of crashing the app.
- webview/state.py: use a private _MISSING sentinel instead of None to mark
  'key not found' in _delete_state_value, fixing a related bug where
  deleting a key whose value is legitimately None incorrectly raised
  AttributeError/KeyError.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@r0x0r

r0x0r commented Oct 19, 2026

Copy link
Copy Markdown
Owner Author

@copilot please fix the merge conflicts in this pull request.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The narrow changes address the reported failures with no blocking issues identified by static review.

No open issues 🎉

What changed in this PR

This PR prevents state synchronization errors from escaping pywebview’s native bridge callbacks and fixes deletion of keys containing None.

Changes:

  • Catch state bridge errors and ignore already-missing delete targets.
  • Distinguish missing keys from legitimate None values using a sentinel.
  • Document the fixes in the changelog.
File Description
webview/​util.py Contain exceptions from state bridge operations.
webview/​state.py Use a sentinel to identify missing keys during deletion.
docs/​CHANGELOG.md Document the state synchronization fixes.

Review effort: Balanced

Co-authored-by: r0x0r <1549657+r0x0r@users.noreply.github.com>

Copilot AI commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

@copilot please fix the merge conflicts in this pull request.

Resolved in 5835c7b by merging the latest master and preserving both branches’ changelog entries. The state-race fix remains intact. Tests and lint could not run because pytest and Ruff are not installed in this environment; the final review found no significant issues.

@r0x0r
r0x0r merged commit 79d4e2c into master Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants