Skip to content

fix: self-heal a corrupt Core Kit store instead of wedging the app - #1183

Merged
FSM1 merged 2 commits into
mainfrom
fix/self-heal-corrupt-corekit-store
Aug 8, 2026
Merged

fix: self-heal a corrupt Core Kit store instead of wedging the app#1183
FSM1 merged 2 commits into
mainfrom
fix/self-heal-corrupt-corekit-store

Conversation

@FSM1

@FSM1 FSM1 commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Problem

AsyncStorage.get parses the Core Kit store with a bare JSON.parse, and init() has no guard around it, so an unreadable corekit_store threw out of restore().

CoreKitProvider handed the same settle to both fulfil and reject, and settled was { session, status: 'ready', error: null }. A rejected restore was therefore indistinguishable from "restored, signed out" — no unhandled rejection, no console surface, no error state. Nothing cleared the bad blob either, so the next login hit the same parse in the SDK's createSession and threw again, surfacing as a raw SyntaxError on the login page. The tab stayed wedged across reloads; the only escape was clicking sign out, whose unconditional clearStore() runs whether or not a session is live.

Change

coreKit.tsWeb3AuthSession.restore() clears the store when the failure was the store itself, reusing the clearStore() that already backs every other teardown path.

The trigger is deliberately narrow. init() does not only read the store: it ends in await this.featureRequest(), a bare fetch of the Web3Auth feature-access endpoint with no try/catch, and its session rehydrate swallows its own failures and falls through to it. An unconditional purge would therefore delete a perfectly good store on an offline reload — origin-wide, so one background tab booting offline would evict the store every other tab's leader promotion depends on. The blob also carries the device factor share once MFA is reachable, which turns that into share destruction rather than a cache eviction. So restore() probes readability against the shape the SDK actually requires — JSON.parse(raw || '{}')[key], i.e. parses and is a non-null object — and purges only when that fails.

CoreKitProvider.tsx — reject and resolve now settle differently, and a rejected restore records the failure.

The tab still lands at the front door and can never present as authenticated: authStore only flips on a completed engine handoff, and init() opens with resetState(), so a throw leaves isLoggedIn() false and the restore effect in useAuth returns early. But it lands there carrying the failure instead of a clean sign-out, and it keeps the session in hand rather than nulling it — signing in again is the way out of this state, and useAuth gates login on the session being present.

The surfaced message is a fixed string, not the underlying throw. V8 quotes the offending input in a JSON.parse failure — JSON.parse('secret-key-bytes') throws Unexpected token 's', "secret-key-bytes" is not valid JSON — and that input is bearer key material, so neither the UI nor the console gets the cause.

Tests

New apps/web/src/auth/CoreKitProvider.test.tsx, mounting the provider over a real createCoreKitSession so the store under test is the real one, with the SDK class faked to reproduce its unguarded store read on both the restore and the login path:

  • discards a store the restore could not read, and does not pass it off as a signed-out tab
  • leaves a login after a failed restore able to establish a session
  • keeps a readable store when the restore failed for some other reason
  • keeps a store the restore read cleanly, session in it or not

Each was proven red by reverting the production code. Against unmodified main the first two fail. With only the provider change reverted, the first fails. With only the purge reverted, the first two fail. With the purge left unconditional, the third fails — expected null to be '{"deviceFactor":"a-device-factor"}'.

Known residual

The recorded failure is latched in provider state for the tab's lifetime, and useAuth returns error ?? coreKitError. auth.error is rendered only by LoginPage, which is mounted only at / and redirects away on authentication, so this is invisible while signed in — but a clean sign-out in the same tab returns to a login page still showing the restore notice. Clearing it needs either new context API or new state in useAuth, both outside a fail-closed fix to the restore path.

Closes #1182

Note

Fix corrupt Core Kit store causing app to wedge on restore failure

  • On Web3AuthSession.restore failure, a new storeIsReadable() check determines whether the local MPC Core Kit store is corrupt; unreadable stores are cleared before rethrowing the error.
  • CoreKitProvider now distinguishes restore failures from success: a rejected restore sets error to a generic RESTORE_FAILED message instead of silently clearing it.
  • Readable stores (e.g. those that fail due to network issues) are preserved so the session can be recovered later.
  • Tests in CoreKitProvider.test.tsx cover truncated JSON, null stores, offline init failures, and successful login after a failed restore.

Macroscope summarized 7669450.

Summary by CodeRabbit

  • Bug Fixes
    • Improved session restoration when saved sign-in data is corrupted or unreadable.
    • Automatically clears invalid saved data so users can sign in again successfully.
    • Preserves valid saved data when restoration fails for unrelated reasons.
    • Displays a restoration error instead of incorrectly showing a clean signed-out state.

`AsyncStorage.get` parses the Core Kit store with a bare `JSON.parse`, and
`init()` has no guard around it, so an unreadable `corekit_store` threw out of
`restore()`. `CoreKitProvider` handed the same `settle` to fulfil and reject,
so that throw was indistinguishable from a clean signed-out restore: no
console surface, no error state, and nothing cleared the blob. The next login
hit the same parse in `createSession` and threw again, wedging the tab across
reloads.

`Web3AuthSession.restore()` now clears the store when the failure was the
store itself, reusing the existing `clearStore()`. The trigger is narrow on
purpose: `init()` also ends in a bare `fetch` for the SDK's feature check, so
an unconditional purge would delete a good store on an offline reload — and
that blob carries the device factor share once MFA is reachable.

`CoreKitProvider` now distinguishes reject from resolve and records the
failure. The tab still lands at the front door and can never present as
authenticated, but it lands there carrying the failure, and it keeps the
session in hand because logging in again is the way out. The message is a
fixed string rather than the underlying throw: V8 quotes the offending input
in a `JSON.parse` failure, and that input is bearer key material.

Closes #1182

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Core Kit restoration now detects malformed persisted state, clears it, and rethrows initialization failures. CoreKitProvider records rejected restoration with RESTORE_FAILED. Tests cover recovery, subsequent login, and readable-store preservation.

Changes

Core Kit restore recovery

Layer / File(s) Summary
Persisted store recovery
apps/web/src/auth/coreKit.ts
restore() validates persisted storage after initialization failure, clears malformed state, and rethrows the original failure.
Restore failure state
apps/web/src/auth/CoreKitProvider.tsx
Rejected restoration now sets status: 'ready' with RESTORE_FAILED; successful restoration clears the error.
Restore recovery validation
apps/web/src/auth/CoreKitProvider.test.tsx
Tests verify corrupt-store cleanup, successful login after failed restoration, and preservation of readable or clean signed-out stores.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CoreKitProvider
  participant coreKit
  participant Web3AuthMPCCoreKit
  participant PersistedStore
  CoreKitProvider->>coreKit: restore()
  coreKit->>Web3AuthMPCCoreKit: initialize()
  Web3AuthMPCCoreKit->>PersistedStore: read persisted state
  PersistedStore-->>Web3AuthMPCCoreKit: initialization result
  alt malformed persisted state
    coreKit->>PersistedStore: clear store
    coreKit-->>CoreKitProvider: restoration failure
    CoreKitProvider->>CoreKitProvider: set RESTORE_FAILED
  else valid persisted state
    coreKit-->>CoreKitProvider: restored session
  end
Loading

Possibly related issues

  • Issue 1175: Both changes modify Core Kit persisted-store handling, including malformed-store detection and cleanup.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue #1182 by detecting corrupt stores, preserving valid stores, recording restore failures, supporting retry login, and adding provider tests.
Out of Scope Changes check ✅ Passed The changes are limited to Core Kit restore handling and focused provider tests required by issue #1182.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: recovering from a corrupt Core Kit store instead of leaving the app unusable.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/self-heal-corrupt-corekit-store

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@FSM1
FSM1 marked this pull request as ready for review August 8, 2026 18:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/auth/CoreKitProvider.test.tsx`:
- Around line 16-25: Update the CoreKitProvider test mock and cases around
readStore to exercise the SDK’s documented parsed-store property access,
including the JSON.parse('null') failure path. Add a NULL_STORE = 'null'
scenario that verifies restore clears the stored value and reports the restore
failure, covering the parsed !== null recovery branch.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 20199d36-9536-4163-a545-9e7e5495aff5

📥 Commits

Reviewing files that changed from the base of the PR and between 390a579 and 0abfa18.

📒 Files selected for processing (3)
  • apps/web/src/auth/CoreKitProvider.test.tsx
  • apps/web/src/auth/CoreKitProvider.tsx
  • apps/web/src/auth/coreKit.ts

Comment thread apps/web/src/auth/CoreKitProvider.test.tsx Outdated
@FSM1
FSM1 marked this pull request as draft August 8, 2026 18:56
The fake SDK only parsed its store, while the real `AsyncStorage.get`
parses and then indexes the result for `sessionId`. A store holding the
JSON `null` literal therefore survived the fake and threw in the SDK, so
the `parsed !== null` arm of `storeIsReadable` was never exercised.

The fake now performs the index, and both corrupt-store tests run over
every shape the SDK's read throws on: a truncated write and a null
literal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@FSM1
FSM1 marked this pull request as ready for review August 8, 2026 19:03
@FSM1
FSM1 enabled auto-merge (squash) August 8, 2026 19:04
@FSM1
FSM1 merged commit f249e17 into main Aug 8, 2026
35 checks passed
@FSM1
FSM1 deleted the fix/self-heal-corrupt-corekit-store branch August 8, 2026 19:11
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.

web: self-heal a corrupt Core Kit store instead of wedging the app

1 participant