Summary
Three related index and null-guard defects in QfcCollectionController's conversation-expansion
path: a -1 index used to subscript _itemGroups, a slot-reservation count that can disagree with
the number of members actually inserted, and an unguarded dereference that precedes its own null
check.
Environment
- OS/version: n/a (logic defects, reproducible wherever QuickFiler runs)
- Python version: n/a
- Command/flags used: n/a
- Data source or fixture:
QuickFiler/Controllers/QfcCollectionController.cs
Steps to Reproduce
Defect 1 - ToggleGroupConv(string) can index _itemGroups[-1] (:1733-1766)
- At
:1743, indexOriginal is -1 when the original message has already been removed.
- That routes to
PromoteFirstChild(originalId, ref childCount) at :1970.
PromoteFirstChild calls FindIndex at :1972. If no group carries
ConvOriginID == originalId, that also returns -1.
- Line 1975 then evaluates
_itemGroups[-1].ItemViewer, throwing ArgumentOutOfRangeException.
ChangeConversationSilently(indexOriginal, true) at :1749 fails the same way for the same
reason.
Defect 2 - EnumerateConversationMembers count mismatch (:1875-1922)
ToggleUnGroupConv reserves insertCount = conversationCount - 1 slots at :1823 and
:1827-1829.
EnumerateConversationMembers iterates insertions.Count at :1888-1889, a count derived
independently from resolver.ConversationItems.SameFolder at :1883-1886.
- If the resolver returns more members than
conversationCount - 1, then
_itemGroups[i + insertionIndex] at :1893 walks past the reserved slots and re-initializes
groups that already hold other messages.
- If it returns fewer, the empty placeholder
QfcItemGroups created at :2008 are left with a
null ItemController, which the next RenumberGroups at :2068 dereferences.
Defect 3 - SetVisualDigits inconsistent null handling (:130-146)
- Line 140 dereferences
grp.ItemController.ItemNumberDigits with no guard.
- Lines 141-142 guard the same object:
grp.ItemController?.ItemNumber... ?? 0.ToString(format).
- Since
ItemController genuinely can be null (see Defect 2), line 140 throws before the guard on
line 141 is ever evaluated.
Expected Behavior
- A
-1 index should be handled explicitly rather than used to subscript _itemGroups.
- The number of slots reserved and the number of members inserted should be derived from a single
source, or reconciled before insertion.
SetVisualDigits should guard ItemController consistently across all three reads.
Actual Behavior
ArgumentOutOfRangeException when a conversation's original message is gone and no child carries
the matching ConvOriginID.
- Either existing groups are silently overwritten, or placeholder groups with a
null
ItemController cause a NullReferenceException in RenumberGroups.
NullReferenceException at line 140 whenever ItemController is null.
Logs / Screenshots
Impact / Severity
All three throw rather than corrupt, so the failure is visible. Defect 2's overwrite branch is the
most serious because it can silently re-initialize a group holding a different message.
Source
From: docs/features/potential/2026-08-07-qfc-collection-conversation-index-defects.md
Summary
Three related index and null-guard defects in
QfcCollectionController's conversation-expansionpath: a
-1index used to subscript_itemGroups, a slot-reservation count that can disagree withthe number of members actually inserted, and an unguarded dereference that precedes its own null
check.
Environment
QuickFiler/Controllers/QfcCollectionController.csSteps to Reproduce
Defect 1 -
ToggleGroupConv(string)can index_itemGroups[-1](:1733-1766):1743,indexOriginalis-1when the original message has already been removed.PromoteFirstChild(originalId, ref childCount)at:1970.PromoteFirstChildcallsFindIndexat:1972. If no group carriesConvOriginID == originalId, that also returns-1._itemGroups[-1].ItemViewer, throwingArgumentOutOfRangeException.ChangeConversationSilently(indexOriginal, true)at:1749fails the same way for the samereason.
Defect 2 -
EnumerateConversationMemberscount mismatch (:1875-1922)ToggleUnGroupConvreservesinsertCount = conversationCount - 1slots at:1823and:1827-1829.EnumerateConversationMembersiteratesinsertions.Countat:1888-1889, a count derivedindependently from
resolver.ConversationItems.SameFolderat:1883-1886.conversationCount - 1, then_itemGroups[i + insertionIndex]at:1893walks past the reserved slots and re-initializesgroups that already hold other messages.
QfcItemGroups created at:2008are left with anullItemController, which the nextRenumberGroupsat:2068dereferences.Defect 3 -
SetVisualDigitsinconsistent null handling (:130-146)grp.ItemController.ItemNumberDigitswith no guard.grp.ItemController?.ItemNumber... ?? 0.ToString(format).ItemControllergenuinely can benull(see Defect 2), line 140 throws before the guard online 141 is ever evaluated.
Expected Behavior
-1index should be handled explicitly rather than used to subscript_itemGroups.source, or reconciled before insertion.
SetVisualDigitsshould guardItemControllerconsistently across all three reads.Actual Behavior
ArgumentOutOfRangeExceptionwhen a conversation's original message is gone and no child carriesthe matching
ConvOriginID.nullItemControllercause aNullReferenceExceptioninRenumberGroups.NullReferenceExceptionat line 140 wheneverItemControlleris null.Logs / Screenshots
research for issue Feature: quickfiler-collection-controller-coverage #454 (epic Feature: quickfiler-80-per-file-coverage #136, child F11); full analysis in
docs/features/active/2026-08-07-quickfiler-collection-controller-coverage-454/research/qfc-collection-controller.mdsections E11, E12, and E13.
Impact / Severity
All three throw rather than corrupt, so the failure is visible. Defect 2's overwrite branch is the
most serious because it can silently re-initialize a group holding a different message.
Source
From: docs/features/potential/2026-08-07-qfc-collection-conversation-index-defects.md