Skip to content

Surface root cause in ErrorMessage for wrapped exceptions#2352

Merged
alexeyzimarev merged 2 commits intodevfrom
fix/innermost-error-message
Feb 26, 2026
Merged

Surface root cause in ErrorMessage for wrapped exceptions#2352
alexeyzimarev merged 2 commits intodevfrom
fix/innermost-error-message

Conversation

@alexeyzimarev
Copy link
Member

@alexeyzimarev alexeyzimarev commented Feb 26, 2026

Summary

  • Use exception.GetBaseException().Message instead of exception.Message for ErrorMessage in both GetErrorResponse and AddException
  • This surfaces the actual root cause (e.g. TLS/SSL errors) instead of generic wrapper messages like "An error occurred while sending the request"
  • The full exception chain remains preserved in ErrorException

Fixes #2278

Test plan

  • Project builds successfully
  • No existing tests assert on ErrorMessage string content
  • Verify TLS error scenarios show specific SSL error messages in ErrorMessage
  • Verify ErrorException still contains the full exception chain

🤖 Generated with Claude Code

…e errors

When HttpClient wraps errors (e.g. TLS/SSL failures), the outer exception
message is generic ("An error occurred while sending the request"). Using
GetBaseException().Message surfaces the actual root cause, making diagnostics
much easier. The full exception chain remains preserved in ErrorException.

Fixes #2278

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@qodo-free-for-open-source-projects
Copy link
Contributor

Review Summary by Qodo

Surface root cause in ErrorMessage for wrapped exceptions

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Surface root cause exception message instead of generic wrapper
• Use GetBaseException().Message for ErrorMessage in error handling
• Preserve full exception chain in ErrorException property
• Improves diagnostics for TLS/SSL and other wrapped exceptions
Diagram
flowchart LR
  A["Exception with wrapper message"] -->|GetBaseException| B["Root cause exception"]
  B -->|Extract Message| C["Specific error details"]
  A -->|Preserve chain| D["ErrorException property"]
  C --> E["ErrorMessage property"]
Loading

Grey Divider

File Changes

1. src/RestSharp/Response/RestResponseBase.cs 🐞 Bug fix +1/-1

Use base exception message in AddException

• Changed AddException method to use exception.GetBaseException().Message instead of
 exception.Message
• Surfaces the innermost exception message to reveal root causes
• Maintains full exception chain in ErrorException field

src/RestSharp/Response/RestResponseBase.cs


2. src/RestSharp/RestClient.Async.cs 🐞 Bug fix +1/-1

Use base exception message in GetErrorResponse

• Updated GetErrorResponse method to use exception.GetBaseException().Message for ErrorMessage
• Replaces generic wrapper message with actual root cause message
• Improves error diagnostics for wrapped exceptions like TLS/SSL failures

src/RestSharp/RestClient.Async.cs


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Contributor

qodo-free-for-open-source-projects bot commented Feb 26, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Remediation recommended

1. Inconsistent timeout classification 🐞 Bug ✓ Correctness
Description
GetErrorResponse now sets ErrorMessage from exception.GetBaseException().Message, but still
classifies TimedOut/Aborted using the outer exception type and checks the outer exception.Message
for a timeout substring. If a timeout/cancellation is wrapped, callers can see a root-cause timeout
message while ResponseStatus is misclassified (e.g., Error/Aborted).
Code

src/RestSharp/RestClient.Async.cs[R65-71]

            ResponseStatus = exception is OperationCanceledException
                ? TimedOut() ? ResponseStatus.TimedOut : ResponseStatus.Aborted
                : ResponseStatus.Error,
-            ErrorMessage   = exception.Message,
+            ErrorMessage   = exception.GetBaseException().Message,
            ErrorException = exception
        };
Evidence
The method mixes base-exception messaging with wrapper-exception classification: ErrorMessage is
derived from GetBaseException(), but ResponseStatus and TimedOut() operate on the original (possibly
wrapping) exception. This can produce inconsistent response semantics when the base exception
differs from the wrapper (the scenario this PR explicitly targets: wrapped exceptions).

src/RestSharp/RestClient.Async.cs[63-75]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`GetErrorResponse` now sets `ErrorMessage` from `exception.GetBaseException().Message`, but timeout/abort detection still uses the *outer* exception (`exception is OperationCanceledException` and `exception.Message.Contains(...)`). With wrapped exceptions, this can misclassify `ResponseStatus` while showing a root-cause message.

### Issue Context
This PR’s goal is to surface the real root cause for wrapped exceptions; status classification should align with the same root cause to avoid inconsistent behavior.

### Fix Focus Areas
- src/RestSharp/RestClient.Async.cs[63-75]

### Suggested direction
- Compute `var baseException = exception.GetBaseException();`
- Use `baseException` (or both `exception` and `baseException`) for:
 - `ResponseStatus` type checks (e.g., cancellation/timeout)
 - `TimedOut()` message checks (if you keep the substring heuristic)
- Consider preferring `timeoutToken.IsCancellationRequested` + exception type checks over message substring matching, if feasible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Feb 26, 2026

Deploying restsharp with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5b1de23
Status: ✅  Deploy successful!
Preview URL: https://c1556ef6.restsharp.pages.dev
Branch Preview URL: https://fix-innermost-error-message.restsharp.pages.dev

View logs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link

@github-actions
Copy link

Test Results

   42 files     42 suites   17m 49s ⏱️
  505 tests   505 ✅ 0 💤 0 ❌
3 530 runs  3 530 ✅ 0 💤 0 ❌

Results for commit 5b1de23.

@alexeyzimarev alexeyzimarev merged commit 2802d8a into dev Feb 26, 2026
12 checks passed
@alexeyzimarev alexeyzimarev deleted the fix/innermost-error-message branch February 26, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cryptic TLS Error messages

1 participant