OpenCut-app / OpenCut-app/OpenCut
[BUG] A failed storage migration permanently poisons retries and can leave saving paused
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 89.8k
- Forks
- 8.9k
- PR merge metrics
- No merged PRs in 30d
Description
Platform
Web editor on current dev (c2e266870172312f461df75da3e7f6fbe9d2a1fc); browser-independent storage initialization failure.
Browser
All supported browsers (deterministic with a migration adapter that rejects once).
Current Behavior
A transient storage migration rejection is cached permanently at two layers, so later project loads cannot retry successfully.
On current dev:
ProjectManager.ensureStorageMigrations()storesstorageMigrationPromiseand never clears it whenrunStorageMigrations()rejects.StorageService.ensureMigrations()likewise retains a rejectedmigrationsPromise.ProjectManager.loadProject()callseditor.save.pause()and awaitsensureStorageMigrations()before entering itstry/finally. If migration rejects, thefinallythat resetsisLoading, notifies, and callseditor.save.resume()is skipped.- Subsequent
loadProject()/loadAllProjects()calls await the same rejected promise rather than rerunning the migration.
After one transient IndexedDB/OPFS migration failure, project loading can remain poisoned for the manager lifetime and autosave can remain paused.
Expected Behavior
Concurrent migration callers should share one in-flight promise, successful completion should remain cached, and a rejected current promise should be cleared so the next explicit load retries. Every path after pausing save should restore loading/migration flags, notify, and resume saving exactly once. Failed initialization should not clear the currently loaded media/scenes or partially publish project state.
Recurrence Probability
Always
Steps To Reproduce
- Configure
runStorageMigrations()to reject its first call and resolve its second. - Call
ProjectManager.loadProject({ id })and observe the first rejection. - Restore the migration dependency and call
loadProject({ id })orloadAllProjects()again. - Observe that the original rejected
storageMigrationPromiseis awaited again, so migration is not retried. - Observe that the first
loadProject()rejected before itstry/finally, leavingeditor.savepaused and initial loading state uncleared. - Repeat at the
StorageService.ensureMigrations()layer; its rejected cache behaves the same way.
Anything else?
Impact: A recoverable local-storage hiccup can make all projects unusable until the application/manager is recreated, while silently disabling further autosaves in the affected session. This is a critical availability and data-durability failure.
Bounded proposed fix: At both migration-cache layers, assign one local promise, share it across concurrent callers, cache fulfillment, and clear rejection only when the cache still points to that promise. In loadProject(), place all work after save.pause() under one try/finally; restore flags/notification and resume exactly once on every outcome. Preserve existing media/scenes and active state until migration and load succeed.
Focused acceptance tests: For both caches, rejection followed by resolution, concurrent calls invoking migration once, fulfilled-cache reuse, and identity-safe rejection cleanup. For loadProject()/loadAllProjects(), assert state preservation, loading/migration flag reset, notifications, and save pause/resume balance on every failure path.
Per the contribution policy, this report is requesting explicit maintainer approval for this candidate before any implementation or PR work begins.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with ProjectManager.ensureStorageMigrations(), ProjectManager.loadProject(), loadAllProjects(), and StorageService.ensureMigrations(); trace how their cached promises and save/loading state are handled. Run or add the focused tests described in the issue for rejection, retry, concurrency, fulfilled-cache reuse, and identity-safe cleanup. Done means failed initialization retries safely, state and flags are preserved or restored, and save pause/resume remains balanced.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100