Skip to content

Prevent manual bolus crashes from missing carb schedule coverage - #2614

Open
damonbayer wants to merge 1 commit into
LoopKit:next-devfrom
damonbayer:dmb/fix-stale-bolus-carb-history
Open

damonbayer wants to merge 1 commit into
LoopKit:next-devfrom
damonbayer:dmb/fix-stale-bolus-carb-history

Conversation

@damonbayer

@damonbayer damonbayer commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Problem and scope

A UUID-matched symbolication of a Loop 3.15.2 (69) crash identifies CarbMath.swift:717 as the immediate failure, with the stack BolusEntryViewModel.update → recommendManualBolus → LoopAlgorithm.run → generatePrediction → CarbMath.map:

Insulin sensitivity and carb ratio timelines must cover carb entry start dates

At least one carb entry had no preceding carb-ratio or insulin-sensitivity value. The calculation includes previously logged carbs even when the user is bolusing without adding or editing carbs.

The reported incident's underlying cause remains unresolved. The reporter clarified that the bolus view was open for only a few minutes and they believe they were bolusing without adding a carb entry. That does not establish the pending-entry aging scenario below. The IPS contains neither the offending input values nor the pending-entry state. This PR adds a recoverable coverage check that applies to stored as well as pending entries, and repairs a separate concrete pending-entry coverage defect found during the investigation. It should prevent the observed precondition trap in the manual recommendation/preview paths, but is not evidence that the incident's source of incomplete history has been identified.

Pending-entry defect found during investigation

fetchData requests carb-ratio history beginning at now − 12 hours − 1 minute, and ISF/override history extends back at least that far. Both recommendManualBolus and the glucose preview then append a pending entry after fetching that history. Its timestamp stays fixed while the history window advances.

For example, select an entry 12 hours in the past and leave the bolus screen open for its five-minute refresh. The pending entry is then four minutes older than the fetched carb-ratio window. closestPrior(to: entry.startDate) returns nil and triggers the precondition above. The earlier ISF-window fix (#2489) is already present, but only covers the normal query window.

A synthetic input against the recorded algorithm revision reproduced SIGTRAP with a carb entry one second before the ratio-history boundary; the boundary itself and extended history succeeded. This establishes the coverage failure mechanism, not the actual input state of the reported incident.

Change

  • Allow fetchData to extend carb-ratio, ISF, and override history to the pending entry's timestamp. Both manual-bolus recommendations and their glucose previews supply that timestamp, including edits.
  • Preserve the ordinary stored-carb and glucose query windows; only the required schedule history is extended.
  • Validate schedule lookups on the final input before the manual recommendation or prediction runs. This check includes previously stored carbs when there is no pending carb entry. If actual history still does not cover an entry, throw the corresponding existing configuration error so the bolus UI clears the recommendation instead of triggering the algorithm's precondition.
  • Make the settings mock truncate these histories at the requested start, matching SettingsStore. Its previous daily-boundary behavior could accidentally conceal this bug.

Validation

Regression coverage includes a pending 12-hour-old entry followed by a five-minute clock advance, an edited entry outside the normal window, historical override application, preservation of normal coverage for recent entries, missing carb-ratio and ISF history in both recommendation and preview paths, and clearing an existing UI recommendation after a configuration error.

Passed 80 tests, 0 failures: all LoopDataManagerTests (20) and BolusEntryViewModelTests (60), including nine new regression tests. Ran on an iPhone 16 / iOS 18.5 simulator with ad-hoc signing, against Loop next-dev at 8ca3f9d8, LoopKit dad7d163, and LoopAlgorithm 9628dfe6.

xcodebuild test -workspace LoopWorkspace.xcworkspace -scheme Loop \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.5' \
  -only-testing:LoopTests/LoopDataManagerTests \
  -only-testing:LoopTests/BolusEntryViewModelTests \
  CODE_SIGNING_ALLOWED=YES CODE_SIGN_IDENTITY=-

git diff --check also passes. No changes to the numerical absorption model, dosing formulas, or production signing settings.

@damonbayer damonbayer changed the title Fix crash when pending carbs outlive the bolus history window Prevent manual bolus crashes from missing carb schedule coverage Oct 26, 2026
@ps2

ps2 commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

I want to land this, but I also really want to understand what happened, and I haven't been able to figure out a theory that explains it.

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.

2 participants