Skip to content

fix: gracefully fall back to base snapshot when patch loading fails#114

Closed
eseidel wants to merge 2 commits intoshorebird/devfrom
fix/graceful-patch-load-failure
Closed

fix: gracefully fall back to base snapshot when patch loading fails#114
eseidel wants to merge 2 commits intoshorebird/devfrom
fix/graceful-patch-load-failure

Conversation

@eseidel
Copy link
Copy Markdown

@eseidel eseidel commented Feb 27, 2026

Summary

  • Changes FML_LOG(FATAL) to FML_LOG(ERROR) in TryLoadFromPatch when PatchCache::GetOrLoad returns nullptr
  • This allows ResolveIsolateData / ResolveIsolateInstructions in dart_snapshot.cc to fall through to SearchMapping and load the base app snapshot instead of crashing

Context

When a native iOS plugin (e.g., native_geofence) spawns a secondary headless FlutterEngine for background work, TryLoadFromPatch attempts to load the .vmcode patch for that engine. If loading fails for any reason (iOS background file access restrictions, Dart_LoadELF rejection, etc.), the FML_LOG(FATAL) kills the entire process via fml::KillProcess().

The fallback path already exists in dart_snapshot.cc — both ResolveIsolateData and ResolveIsolateInstructions call TryLoadFromPatch first, then fall through to SearchMapping if it returns nullptr. The FATAL log prevented this fallback from ever executing.

With this fix, the secondary engine runs unpatched (using the base snapshot) rather than crashing the app.

Fixes shorebirdtech/shorebird#3634

Test plan

  • Verify existing patch loading still works for the primary engine (cache hit path unchanged)
  • Verify that a secondary FlutterEngine spawned by a native plugin falls back to base snapshot instead of crashing

When a native iOS plugin (e.g., native_geofence) spawns a secondary
headless FlutterEngine, TryLoadFromPatch attempts to load the patch
for the secondary engine. If loading fails (e.g., due to iOS background
restrictions on file access or Dart_LoadELF rejection), the previous
FML_LOG(FATAL) would kill the entire process.

Change the log level from FATAL to ERROR and allow the nullptr return
to propagate. ResolveIsolateData/ResolveIsolateInstructions in
dart_snapshot.cc already handle nullptr from TryLoadFromPatch by falling
through to SearchMapping, which loads the base app snapshot. The
secondary engine will run unpatched rather than crashing the app.

Fixes shorebirdtech/shorebird#3634
@eseidel
Copy link
Copy Markdown
Author

eseidel commented Feb 27, 2026

Closing for now — need more information from the reporter about the actual failure mode before making a fix. The ERROR logs from PatchCacheEntry::Create (lines 35 and 56 of patch_cache.cc) should appear before the FATAL crash and tell us exactly why the patch load fails.

@eseidel eseidel closed this Feb 27, 2026
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.

fix: iOS SIGABRT in TryLoadFromPatch when native iOS plugins spawn headless FlutterEngine (native_geofence)

1 participant