From 1d27a5dc063cfecc60c538e8254a1a9945cee6dd Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Tue, 11 Aug 2026 14:22:37 -0500 Subject: [PATCH] Fix CarbMath preconditionFailure when a carb entry predates the ISF window CarbMath.map(to:) preconditionFailures if the insulin-sensitivity or carb-ratio timeline doesn't cover a carb entry's start date. The carb-ratio window and carb entries both extend back to carbsStart (baseTime - dateAdjustmentPast), but the ISF window came from timelineIntervalForSensitivity, which is derived from dose and glucose history only. When that history is more recent than carbsStart (e.g. a CGM gap, fresh setup, or a heavily backdated carb entry on the manual-bolus screen), an older carb entry has carb-ratio coverage but no ISF coverage -> closestPrior returns nil -> crash (seen via recommendManualBolus). Extend the ISF and override history back to min(neededSensitivityTimeline.start, carbsStart) so it covers the same carb window as carbRatio. --- Loop/Managers/LoopDataManager.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index 613d3695b..71ebfb102 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -389,8 +389,15 @@ final class LoopDataManager: ObservableObject { recommendationEffectInterval: recommendationEffectInterval ) + // Carb entries (and a backdated manual-bolus entry) can extend back to carbsStart, and + // CarbMath.map(to:) preconditionFailures if the ISF/carb-ratio timelines don't cover every + // carb entry's start date. timelineIntervalForSensitivity derives its window from dose and + // glucose history only — which can be more recent than carbsStart (e.g. after a CGM gap) — + // so extend the ISF (and override) window back to cover the carb window, matching carbRatio. + let sensitivityStart = min(neededSensitivityTimeline.start, carbsStart) + let sensitivity = try await settingsProvider.getInsulinSensitivityHistory( - startDate: neededSensitivityTimeline.start, + startDate: sensitivityStart, endDate: neededSensitivityTimeline.end ) @@ -404,7 +411,7 @@ final class LoopDataManager: ObservableObject { throw LoopError.configurationError(.maximumBasalRatePerHour) } - var overrides = temporaryPresetsManager.presetHistory.getOverrideHistory(startDate: neededSensitivityTimeline.start, endDate: forecastEndTime) + var overrides = temporaryPresetsManager.presetHistory.getOverrideHistory(startDate: sensitivityStart, endDate: forecastEndTime) // For recommendation, we should consider preMeal override to be ending at time of dose if presumePresetEndingNow,