Skip to content

fix(build): turn off Xcode's Thread Performance Checker in the iOS scheme - #6882

Merged
FeodorFitsner merged 3 commits into
mainfrom
fix/ios-thread-performance-checker
Sep 26, 2026
Merged

FeodorFitsner merged 3 commits into
mainfrom
fix/ios-thread-performance-checker

Conversation

@ndonkoHenri

@ndonkoHenri ndonkoHenri commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #5480

Problem

Apps built with flet build that use sqlite3 stop when run from Xcode on iOS 18 or later:

Assertion failed: (0), function isBulkReadStatement, file SQLiteDatabaseTracking.cpp, line 711.

The assert isn't in SQLite or Flet. It's in libRPAC.dylib, Xcode's Thread Performance Checker, which Xcode's Run injects into the app and, for iOS 18+ destinations, configures with its launch disk-write checks on (PERFC_ENABLE_AGPC_DISKWRITES_LAUNCH_CHECKS=1). On iOS the library wraps 19 sqlite3_* functions: the close wrapper stops tracking a connection before closing it, and the finalize wrapper asserts when a statement's connection isn't tracked. So any sqlite3_finalize() after a close aborts, although SQLite allows it. Even sqlite3_close() returning SQLITE_BUSY, with the connection still open, followed by sqlite3_finalize() aborts.

CPython (the same code in 3.12, 3.13 and 3.14) finalizes after closing when:

  • a connection is garbage-collected instead of closed. Its statement cache references it, so the cycle collector frees it, and the abort lands at the next collection rather than where the connection went out of scope;
  • with sqlite3.connect(...) is used, which commits but doesn't close;
  • a connection is closed while a cursor still has unread rows.

An explicit close() finalizes statements first, which is why the snippet posted in the issue doesn't crash.

Nothing outside Xcode's Run loads the checker: flutter run/flet debug on a simulator, home-screen launches, TestFlight and the App Store are unaffected. Xcode turns these checks on only for iOS 18+ destinations, which is why downgrading the simulator runtime "fixed" it in the issue comments. With Xcode 26+, flet debug ios on a physical device launches through LLDB (Flutter's lldbDebugging, on by default) and only falls back to Xcode's Run if that fails.

Changes

  • iOS build template Runner.xcscheme: the LaunchAction sets disablePerformanceAntipatternChecker = "YES", the setting behind the Thread Performance Checker checkbox in the scheme's Diagnostics tab. Only Xcode's Run uses it; builds and archives are unchanged. Anyone who wants the checker can turn it back on in the scheme editor.
  • Changelog entry.

No docs change: flet build re-renders the template when the Flet version changes, so existing projects pick this up on upgrade.

Why not the alternatives

  • PERFC_ENABLE_SQLITE_WRITE_STATEMENT_INSPECTION=0 in the scheme's environment: also fixes it (verified) and keeps the checker's other warnings, but it's an undocumented internal switch that Apple can rename.
  • Linking SQLite statically into _sqlite3: the checker would no longer see the calls, but it adds app size and CVE upkeep for a debug-only problem.
  • client/ios: doesn't embed Python, and the checker's warnings are useful to people working on the client natively, so it's unchanged.

Testing

Xcode 27.0 (27A266a), iOS 18.6 simulator (iPhone 16 Pro), Python 3.14.7, an app built with flet build ios-simulator and started with Xcode's Run action (driven over JXA, as flutter_tools does):

Scenario Current scheme With this change
Connection never closed aborts runs
with sqlite3.connect() aborts runs
close() with unread rows aborts¹ runs

¹ Launched with the environment Xcode injects (simctl launch) rather than through Xcode itself.

  • Current scheme: an LLDB breakpoint on __assert_rtn caught isBulkReadStatement (SQLiteDatabaseTracking.cpp, line 711), called from libRPAC's interposed_sqlite3_finalize, called from _sqlite3. The code from the issue runs even with the current scheme, since it closes explicitly.
  • With this change: libRPAC isn't loaded, and the scheme's custom LLDB init file still loads, so debugging is unaffected.
  • The attribute survives Flutter's scheme migrations (they insert customLLDBInitFile and enableGPUValidationMode as strings) and another flet build; Xcode doesn't rewrite it.
  • A standalone C program reproduces the assert on the iOS 18.6, 26.3 and 26.5 simulator runtimes under the injected environment, and passes without the checker or with the SQLite inspection turned off.

Summary by Sourcery

Disable Xcode’s Thread Performance Checker in generated iOS schemes to prevent sqlite3-related crashes during Xcode launches.

Bug Fixes:

  • Prevent iOS apps using sqlite3 from aborting when launched from Xcode on iOS 18 and later by disabling the incompatible Thread Performance Checker in generated Xcode schemes.

Chores:

  • Document the iOS Xcode launch fix in the changelog.

Xcode's Run loads the Thread Performance Checker into apps it launches on
iOS 18 and later. Its SQLite tracking aborts with `Assertion failed: (0),
function isBulkReadStatement, file SQLiteDatabaseTracking.cpp, line 711`
when a statement is finalized after its connection was closed. SQLite
allows that order, and CPython produces it when it garbage-collects an
unclosed connection or when a connection is closed while a cursor still
has unread rows, so apps that use `sqlite3` stopped when run from Xcode.

The generated scheme now sets `disablePerformanceAntipatternChecker`, the
setting behind the checker's checkbox in the scheme's Diagnostics tab.
Apps that are not launched by Xcode never load the checker.

@sourcery-ai sourcery-ai Bot 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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Deploying flet-website-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: f172eef
Status: ✅  Deploy successful!
Preview URL: https://445d3a51.flet-website-v2.pages.dev
Branch Preview URL: https://fix-ios-thread-performance-c.flet-website-v2.pages.dev

View logs

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.

Copilot review overview

🔵 Needs a closer look

The scheme setting needs an automated regression test before approval.

Review effort: Balanced
Findings: None

What changed in this PR

Disables Xcode’s Thread Performance Checker in generated iOS schemes to prevent SQLite assertion failures during Xcode launches.

Changes:

  • Adds disablePerformanceAntipatternChecker = "YES" to the iOS LaunchAction.
  • Documents the fix in the changelog.
File Review
sdk/​python/​templates/​build/​{{cookiecutter.out_dir}}/​ios/​Runner.xcodeproj/​xcshareddata/​xcschemes/​Runner.xcscheme Disables the checker; needs a template-contract regression test.
CHANGELOG.md Records the fix; wording could be clearer.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@FeodorFitsner
FeodorFitsner merged commit 5124014 into main Sep 26, 2026
2 of 79 checks passed
@FeodorFitsner
FeodorFitsner deleted the fix/ios-thread-performance-checker branch September 26, 2026 02:13
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.

[Bug] iOS build fails with "Assertion failed: isBulkReadStatement" when using standard sqlite3

3 participants