Skip to content

Bug: fileio2-write-retry-reports-success-on-final-failure #647

Description

@drmoisan
  • Work Mode: full-bug

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:

  1. 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.
  2. 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

  1. Open the intended target file exclusively in another process and keep the handle.
  2. Call FileIO2.WriteTextFileAsync against that path.
  3. 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

  • Attached minimal logs or snippet
  • Snippet: the retry loop sits at UtilitiesCS/To Depricate/FileIO2.cs:50-89; the final-failure path
    logs the exception and then assigns the success flag true before returning.

Impact / Severity

  • Blocker
  • High
  • Medium
  • Low

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions