Summary
FileIO2.WriteTextFileAsync in UtilitiesCS/To Depricate/FileIO2.cs retries on IOException up to
100 times with a 100 millisecond delay between attempts, roughly a ten-second bounded window. When
the final attempt still fails it logs the exception and then sets its success flag to true and
returns, so the caller cannot distinguish a completed write from a write that never happened.
Two consequences:
- A persistently failed write is silent. Any caller that awaits this method and treats normal
return as success is wrong, and there is no return value or exception that would let it behave
otherwise.
- The retry window is not cancellable. The loop's delay does not observe a
CancellationToken,
so a caller that awaits the method while the target file is locked is stalled for the whole
bounded window regardless of what its own token does.
The second consequence became reachable in a new place through issue #442. QfcHomeController.WriteMetricsAsync
now awaits this writer directly, and it deliberately passes CancellationToken.None so that a
session cancellation cannot destroy the metrics write. That choice is correct for its own purpose,
but it means a locked session-metrics file stalls the awaiting continuation for the full window with
no cancellation path.
FileIO2.cs was not modified by #442 and is outside that feature's owned files. This is recorded
as a pre-existing defect in a module already marked for deprecation, surfaced by that work rather
than caused by it. Feature-review raised it as finding CR-2 (Minor, pre-existing, non-blocking) and
explicitly recommended the promotion lifecycle rather than an in-scope fix.
Environment
- OS/version: Windows 11, Outlook VSTO add-in host
- Python version: not applicable (C# / .NET Framework 4.8)
- Command/flags used:
vstest.console.exe UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll
- Data source or fixture: any file held open exclusively by another process while the write is attempted
Steps to Reproduce
- Open the intended target file exclusively in another process and keep the handle.
- Call
FileIO2.WriteTextFileAsync against that path.
- Wait for the retry window to expire, then observe the method's return and the file's contents.
Expected Behavior
Exhausting the retry budget is a failure and must be reported as one: either by throwing, or by
returning a result the caller can inspect. The retry delay should also observe a supplied
CancellationToken so a caller can abandon the attempt.
Actual Behavior
The method logs and returns normally. The caller has no way to learn the write did not happen, and
the delay is uncancellable for the duration of the window.
Logs / Screenshots
Impact / Severity
Medium. Silent data loss on a genuinely contended file, and an uncancellable multi-second stall in
any await path that reaches it. Both are bounded and neither is reachable from unit tests, because
every current in-repo caller of consequence writes through an injectable seam that tests substitute.
Source
From: docs/features/potential/2026-08-27-fileio2-write-retry-reports-success-on-final-failure.md
Summary
FileIO2.WriteTextFileAsyncinUtilitiesCS/To Depricate/FileIO2.csretries onIOExceptionup to100 times with a 100 millisecond delay between attempts, roughly a ten-second bounded window. When
the final attempt still fails it logs the exception and then sets its success flag to
trueandreturns, so the caller cannot distinguish a completed write from a write that never happened.
Two consequences:
return as success is wrong, and there is no return value or exception that would let it behave
otherwise.
CancellationToken,so a caller that awaits the method while the target file is locked is stalled for the whole
bounded window regardless of what its own token does.
The second consequence became reachable in a new place through issue #442.
QfcHomeController.WriteMetricsAsyncnow awaits this writer directly, and it deliberately passes
CancellationToken.Noneso that asession cancellation cannot destroy the metrics write. That choice is correct for its own purpose,
but it means a locked session-metrics file stalls the awaiting continuation for the full window with
no cancellation path.
FileIO2.cswas not modified by #442 and is outside that feature's owned files. This is recordedas a pre-existing defect in a module already marked for deprecation, surfaced by that work rather
than caused by it. Feature-review raised it as finding CR-2 (Minor, pre-existing, non-blocking) and
explicitly recommended the promotion lifecycle rather than an in-scope fix.
Environment
vstest.console.exe UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dllSteps to Reproduce
FileIO2.WriteTextFileAsyncagainst that path.Expected Behavior
Exhausting the retry budget is a failure and must be reported as one: either by throwing, or by
returning a result the caller can inspect. The retry delay should also observe a supplied
CancellationTokenso a caller can abandon the attempt.Actual Behavior
The method logs and returns normally. The caller has no way to learn the write did not happen, and
the delay is uncancellable for the duration of the window.
Logs / Screenshots
UtilitiesCS/To Depricate/FileIO2.cs:50-89; the final-failure pathlogs the exception and then assigns the success flag
truebefore returning.Impact / Severity
Medium. Silent data loss on a genuinely contended file, and an uncancellable multi-second stall in
any
awaitpath that reaches it. Both are bounded and neither is reachable from unit tests, becauseevery current in-repo caller of consequence writes through an injectable seam that tests substitute.
Source
From: docs/features/potential/2026-08-27-fileio2-write-retry-reports-success-on-final-failure.md