Skip to content

Bug: timeouttask-runwithtimeout-exception-type-mismatch #285

Description

@drmoisan
  • Work Mode: full-bug

Summary

TimeOutTask.RunWithTimeout<T1, TResult> (UtilitiesCS/Threading/TimeOutTask.cs:164-199) catches TimeoutException in its retry/timeout handling ladder, but the wrapped call is actually cancelled via a CancellationTokenSource-driven timeout, which surfaces as TaskCanceledException, not TimeoutException. The catch clause can never match a genuine timer-driven timeout in this overload.

Environment

  • OS/version: n/a (pure C#/.NET Framework logic defect, reproducible on any platform)
  • Python version: n/a
  • Command/flags used: n/a
  • Data source or fixture: UtilitiesCS/Threading/TimeOutTask.cs

Steps to Reproduce

  1. Call RunWithTimeout<T1, TResult> (the Func<T1, TResult> overload declared at TimeOutTask.cs:164) with a milliseconds value short enough that the internal CancellationTokenSource-linked Task.Run is cancelled by the timer before the delegate completes.
  2. Observe that the timer-driven cancellation raises System.Threading.Tasks.TaskCanceledException, not System.TimeoutException.
  3. The catch (TimeoutException) clause at line 199 does not match, so the intended retry/timeout-handling branch never executes for a real timeout; the exception instead propagates unhandled to the caller.
  4. Contrast with every sibling overload in the same file, which correctly catches TaskCanceledException for the identical CancellationTokenSource-driven pattern: RunWithTimeout<TResult> (Func<TResult> overload) at line 64, RunWithTimeout<TResult> (Func<CancellationToken,Task<TResult>> overload) at line 129, RunWithTimeout<T1,T2,TResult> at line 350, RunWithTimeout<T1,T2,T3,TResult> at line 580.

Expected Behavior

A genuine timer-driven timeout in RunWithTimeout<T1, TResult> should be caught and handled by the same retry ladder as every sibling overload (catch (TaskCanceledException)), so maxAttempts/strict retry behavior applies consistently across all RunWithTimeout overloads.

Actual Behavior

catch (TimeoutException) at line 199 only matches a TimeoutException thrown directly by the wrapped delegate itself (an unusual, non-default case). It never matches the actual timer-driven TaskCanceledException that Task.Run(..., combinedToken) raises when the internal CancellationTokenSource fires. TaskCanceledException and TimeoutException both derive independently from SystemException and are unrelated types. As a result, the overload's own existing test coverage simulates "timeout" by having the wrapped delegate throw TimeoutException directly, rather than via a genuinely short milliseconds value - masking the defect in the existing test suite.

Logs / Screenshots

  • Attached minimal logs or screenshot
  • Snippet: Confirmed directly against source at UtilitiesCS/Threading/TimeOutTask.cs:164 (declaration) and :199 (catch (TimeoutException)), compared against sibling catch (TaskCanceledException) clauses at lines 64, 129, 350, 580. Originally identified and documented in docs/features/active/2026-07-07-onedrive-writer-timeout-test-determinism-253/research/2026-07-07T13-00-onedrive-writer-timeout-research.md (Section 2.1) and docs/features/archive/2026-07-07-onedrive-writer-timeout-test-determinism-253/evidence/other/follow-up-issue-note.2026-07-07T14-05.md, both of which explicitly deferred this defect as out of scope for issue Bug: onedrive-writer-timeout-test-determinism #253 and recommended filing a new issue. No open GitHub issue currently references TimeOutTask (verified via gh issue list --search "TimeOutTask").

Impact / Severity

  • Blocker
  • High
  • Medium
  • Low

The retry/timeout-handling contract for this one overload silently does not work for its primary intended case (a real timer-driven timeout); callers relying on maxAttempts/strict retry-on-timeout behavior for RunWithTimeout<T1, TResult> get an unhandled TaskCanceledException instead of a retry.

Source

From: docs/features/potential/2026-07-09-timeouttask-runwithtimeout-exception-type-mismatch.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