Skip to content

Fix Jetpack backup import using stale .ht.sqlite instead of SQL dumps#3144

Merged
ivan-ottinger merged 4 commits intotrunkfrom
fix-jetpack-backup-import-with-stale-sqlite
Apr 21, 2026
Merged

Fix Jetpack backup import using stale .ht.sqlite instead of SQL dumps#3144
ivan-ottinger merged 4 commits intotrunkfrom
fix-jetpack-backup-import-with-stale-sqlite

Conversation

@ivan-ottinger
Copy link
Copy Markdown
Contributor

@ivan-ottinger ivan-ottinger commented Apr 20, 2026

Related issues

  • Fixes STU-1586
  • Related discussions: p1776276319792059/1776002214.354309-slack-C08AHBHGDEZ, p1776442125372719-slack-C0ASQG4MR5Y and p1776691752584329/1776433113.368459-slack-C029FLTM2

Proposed Changes

  • PlaygroundValidator: Skip matching when the backup also contains SQL dumps (sql/ directory), deferring to JetpackValidator which uses the SQL dumps as the authoritative data source
  • JetpackValidator: Exclude wp-content/database/ from the wp-content file copy list as a defensive measure, following the same pattern used in moveExistingWpContentToTrash

Context

When a Jetpack backup contains both SQL dumps (sql/*.sql) and a wp-content/database/.ht.sqlite file, PlaygroundValidator matches first (it's checked before JetpackValidator in the importer options) and copies the stale SQLite file directly as the database — completely ignoring the SQL dumps.

This happens when a site was previously used with Studio, leaving a .ht.sqlite in wp-content/ that gets swept into subsequent Jetpack backups from the production MySQL site. The .ht.sqlite may be weeks or months out of date, causing silent data loss — missing posts, revisions, and outdated page content with no error reported.

I was also checking how that old .ht.sqlite DB file could have gotten to the Jetpack backup. For now it looks like it was some manual process the user took (as they were moving sites previously). I don't see any way we would have included the file in there ourselves. Maybe there was some bug in Studio previously, but I don't see any issues on that front at the moment.

Testing Instructions

  1. Check out the PR branch and build the app with npm start.
  2. Take a Jetpack backup from a WordPress site that includes wp-content/database/.ht.sqlite. You can use real example backups from here: 3aff6-pb.
  3. Import the backup into a Studio site.
  4. Verify all posts, revisions, and page content match the related production site.
  5. You can try to repeat the above steps with trunk checked out and see how the imported site would be different from the production site.

How AI was used in this PR

Claude Code investigated why Jetpack backup imports produced sites with missing posts. Through iterative debugging — with manual testing and verification at each step — we traced the root cause to PlaygroundValidator matching a stale .ht.sqlite before JetpackValidator, and implemented the fix.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

When a Jetpack backup contains both SQL dumps (sql/*.sql) and a
wp-content/database/.ht.sqlite file, PlaygroundValidator matches first
and copies the stale SQLite file directly—ignoring the SQL dumps that
contain the actual up-to-date data.

This happens when a site was previously used with Studio, leaving a
.ht.sqlite in wp-content/ that gets swept into subsequent Jetpack
backups from the production MySQL site.

Two fixes:
- PlaygroundValidator now defers to JetpackValidator when SQL dumps
  are present alongside .ht.sqlite
- JetpackValidator now excludes wp-content/database/ from the file
  copy list, preventing the stale .ht.sqlite from overwriting the
  freshly imported database
@ivan-ottinger ivan-ottinger self-assigned this Apr 20, 2026
@ivan-ottinger ivan-ottinger marked this pull request as ready for review April 20, 2026 13:14
@ivan-ottinger ivan-ottinger requested a review from a team April 20, 2026 13:35
Copy link
Copy Markdown
Contributor

@katinthehatsite katinthehatsite left a comment

Choose a reason for hiding this comment

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

The changes look good to me and I was able to import the file correctly. I also did not see any further regressions 👍

Do you think we should add some tests to cover for this scenario?

@ivan-ottinger
Copy link
Copy Markdown
Contributor Author

Thank you for the review, Kat!

The changes look good to me and I was able to import the file correctly. I also did not see any further regressions 👍

Do you think we should add some tests to cover for this scenario?

Good idea! I have added them in 1cc5ac3.

@wpmobilebot
Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 1cc5ac3 vs trunk

app-size

Metric trunk 1cc5ac3 Diff Change
App Size (Mac) 1439.38 MB 1439.38 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk 1cc5ac3 Diff Change
load 1883 ms 1876 ms 7 ms ⚪ 0.0%

site-startup

Metric trunk 1cc5ac3 Diff Change
siteCreation 8120 ms 8108 ms 12 ms ⚪ 0.0%
siteStartup 4948 ms 4954 ms +6 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

Copy link
Copy Markdown
Contributor

@epeicher epeicher left a comment

Choose a reason for hiding this comment

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

Thanks @ivan-ottinger for the fix, this was a great sleuthing! I have tested it, and I can confirm that importing one of the suggested sites works as expected on this branch compared with the production site, while on trunk, the imported site is different. Changes also LGTM! :shipit:

@ivan-ottinger ivan-ottinger merged commit a8a367a into trunk Apr 21, 2026
10 checks passed
@ivan-ottinger ivan-ottinger deleted the fix-jetpack-backup-import-with-stale-sqlite branch April 21, 2026 07:56
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.

4 participants