Skip to content

CodeQL 5: refactor(ClinicalScheduler): catch specific exceptions in services#193

Open
rlorenzo wants to merge 3 commits into
mainfrom
codeql/5-clinicalscheduler-exceptions
Open

CodeQL 5: refactor(ClinicalScheduler): catch specific exceptions in services#193
rlorenzo wants to merge 3 commits into
mainfrom
codeql/5-clinicalscheduler-exceptions

Conversation

@rlorenzo
Copy link
Copy Markdown
Contributor

@rlorenzo rlorenzo commented May 13, 2026

Summary

Closes ~39 of 43 cs/catch-of-all-exceptions alerts in web/Areas/ClinicalScheduler/Services/**. Replaces blanket catch (Exception) with a when filter that restricts to the exception families these service methods actually need to wrap (DbUpdateException, SqlException, InvalidOperationException) per the CLAUDE.md convention.

Why 4 broad catches are kept

Four catches wrap fire-and-forget post-transaction work - email notifications and audit logging that must run after a successful DB change and must not roll the change back. The EmailNotificationTest.RemoveInstructorScheduleAsync_EmailServiceFails_StillCompletesRemoval test enforces this resilience contract by throwing a raw Exception from the mocked email service and asserting that the schedule removal still succeeds. These four sites are kept broad and explicitly annotated:

#pragma warning disable CA1031 // Intentional broad catch: post-transaction work (email/audit notifications) must not roll back the successful database changes above.
catch (Exception postTransactionEx)
#pragma warning restore CA1031

CodeQL will still flag these four; they're known-intentional and can be dismissed in the dashboard.

Cancellation handling

The when filters originally included OperationCanceledException, which caused cancellation to be wrapped as InvalidOperationException (or, in SchedulePermissionService, silently returned false/empty). Removed from all filters so cooperative cancellation now propagates naturally to callers — a behavior change addressing review feedback.

Files changed

  • InstructorScheduleService.cs - 1 catch narrowed
  • PersonService.cs - 6 catches narrowed
  • RotationService.cs - 7 catches narrowed
  • ScheduleAuditService.cs - 3 catches narrowed
  • ScheduleEditService.cs - 9 narrowed, 4 kept broad with pragma
  • SchedulePermissionService.cs - 8 catches narrowed
  • StudentScheduleService.cs - 1 catch narrowed
  • WeekService.cs - 4 catches narrowed

Context

Fifth in the CodeQL N: cleanup series (after #189, #190, #191, #192).

Test plan

  • npm run test:backend - 1946 tests passing (including the email-failure resilience test)
  • npm run verify:build - clean (0 errors)
  • Pre-commit lint+test+verify all passed
  • CodeQL workflow on this PR shows ~39 of the 43 ClinicalScheduler/Services catch-of-all alerts closed

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Multiple ClinicalScheduler services add Microsoft.Data.SqlClient imports (where needed) and replace broad catch-all exception handlers with filtered catches that only handle DbUpdateException, SqlException, and InvalidOperationException; ScheduleEditService preserves broad post-transaction catches annotated with CA1031.

Changes

Exception handler narrowing across ClinicalScheduler services

Layer / File(s) Summary
Filtered exception handling in simpler services
InstructorScheduleService.cs, PersonService.cs, StudentScheduleService.cs, WeekService.cs
Adds Microsoft.Data.SqlClient where required and replaces broad catch (Exception) blocks with filtered catch clauses handling only DbUpdateException, SqlException, and InvalidOperationException in retrieval methods across these services.
Multi-method filtered exception handling
RotationService.cs, ScheduleAuditService.cs, SchedulePermissionService.cs
Adds Microsoft.Data.SqlClient where required and applies the same filtered catch pattern (DbUpdateException, SqlException, InvalidOperationException) across multiple methods in each service (audit, rotation, and permission-related methods).
ScheduleEditService with CA1031 pragmas
ScheduleEditService.cs
Adds Microsoft.Data.SqlClient, narrows database/error catches to the filtered exception set across many methods, and annotates intentional broad post-transaction catches (notifications/audit/email sending) with #pragma warning disable CA1031 so those failures are logged but do not affect committed transactions.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately reflects the main change: refactoring catch blocks in ClinicalScheduler services to handle specific exceptions instead of catching all exceptions.
Docstring Coverage ✅ Passed Docstring coverage is 83.78% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description clearly describes the changeset: narrowing broad exception catches in 8 service files, retaining 4 intentional broad catches with pragma, and addressing CodeQL alerts.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codeql/5-clinicalscheduler-exceptions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 13, 2026

Codecov Report

❌ Patch coverage is 0% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 42.96%. Comparing base (5949e52) to head (39e16db).

Files with missing lines Patch % Lines
.../ClinicalScheduler/Services/ScheduleEditService.cs 0.00% 9 Missing ⚠️
...calScheduler/Services/SchedulePermissionService.cs 0.00% 8 Missing ⚠️
...reas/ClinicalScheduler/Services/RotationService.cs 0.00% 7 Missing ⚠️
.../Areas/ClinicalScheduler/Services/PersonService.cs 0.00% 6 Missing ⚠️
...eb/Areas/ClinicalScheduler/Services/WeekService.cs 0.00% 4 Missing ⚠️
...ClinicalScheduler/Services/ScheduleAuditService.cs 0.00% 3 Missing ⚠️
...calScheduler/Services/InstructorScheduleService.cs 0.00% 1 Missing ⚠️
...inicalScheduler/Services/StudentScheduleService.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #193   +/-   ##
=======================================
  Coverage   42.96%   42.96%           
=======================================
  Files         877      877           
  Lines       51468    51468           
  Branches     4802     4802           
=======================================
  Hits        22113    22113           
  Misses      28831    28831           
  Partials      524      524           
Flag Coverage Δ
backend 43.04% <0.00%> (ø)
frontend 41.34% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rlorenzo
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
web/Areas/ClinicalScheduler/Services/InstructorScheduleService.cs (1)

108-112: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove OperationCanceledException from the catch filter.

Wrapping OperationCanceledException into InvalidOperationException breaks cancellation flow. Callers expecting cancellation signals will not receive them. Let OperationCanceledException propagate; catch and wrap only the true data-access failures.

Proposed fix
-            catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException)
+            catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException)
             {
                 _logger.LogError(ex, "Error retrieving instructor schedules");
                 throw new InvalidOperationException("Failed to retrieve instructor schedules", ex);
             }

Note: This pattern appears across multiple ClinicalScheduler service files. Apply the same fix wherever this catch filter pattern exists.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/Areas/ClinicalScheduler/Services/InstructorScheduleService.cs` around
lines 108 - 112, The catch filter in InstructorScheduleService (the catch block
handling exceptions around retrieving instructor schedules) incorrectly includes
OperationCanceledException, which should be allowed to propagate; update the
catch filter to remove OperationCanceledException so you only catch
DbUpdateException, SqlException and InvalidOperationException, keep the
_logger.LogError call and rethrow a wrapped InvalidOperationException as before,
and apply the same removal of OperationCanceledException from identical catch
filters across other ClinicalScheduler service classes.
web/Areas/ClinicalScheduler/Services/ScheduleEditService.cs (1)

244-261: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Exclude OperationCanceledException from filtered catches to preserve cancellation semantics.

These catch filters at lines 244, 362, 473, 498, 540, 568, 671, 704, and 729 include OperationCanceledException, which causes cancellation signals to be logged and wrapped as ordinary failures. This breaks async/await cancellation propagation. Add an explicit passthrough catch for OperationCanceledException before each filtered catch:

catch (OperationCanceledException)
{
    throw;
}
catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException)
{
    // existing handler
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/Areas/ClinicalScheduler/Services/ScheduleEditService.cs` around lines 244
- 261, In ScheduleEditService.cs update the catch blocks that currently filter
on OperationCanceledException (the try/catch around the DB save where variables
like mothraId, rotationId, weekIds and LogSanitizer are used) so cancellation is
not swallowed: add an explicit catch (OperationCanceledException) { throw; }
immediately before the existing filtered catch and remove
OperationCanceledException from the when(...) filter (so the filtered catch only
checks DbUpdateException, SqlException, InvalidOperationException); keep the
current logging and InvalidOperationException wrapping logic unchanged in the
filtered catch.
web/Areas/ClinicalScheduler/Services/SchedulePermissionService.cs (1)

91-96: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Separate cancellation from failure handling—don't suppress OperationCanceledException.

All 8 methods catch OperationCanceledException alongside database/operational failures and return false/empty/default values. This masks cancellation as permission denial or missing data, breaking async semantics. Since all methods accept and pass CancellationToken, cancellation is intentional and must propagate to callers.

Add a dedicated catch for OperationCanceledException that rethrows, before the catch block that handles actual database failures. Apply to all 8 methods: 91–96, 118–123, 154–159, 187–192, 216–220, 262–268, 308–313, 350–355.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/Areas/ClinicalScheduler/Services/SchedulePermissionService.cs` around
lines 91 - 96, In SchedulePermissionService, do not swallow
OperationCanceledException in the multi-exception catch; add an explicit catch
(OperationCanceledException) { throw; } immediately before the existing catch
(Exception ex) when (ex is DbUpdateException or SqlException or
InvalidOperationException or OperationCanceledException) so cancellation is
rethrown to callers; update all eight methods that currently catch
OperationCanceledException (the blocks that call _userHelper.GetCurrentUser()
and _logger.LogError(...)) to follow this pattern so only genuine DB/operational
exceptions are handled while cancellations propagate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@web/Areas/ClinicalScheduler/Services/InstructorScheduleService.cs`:
- Around line 108-112: The catch filter in InstructorScheduleService (the catch
block handling exceptions around retrieving instructor schedules) incorrectly
includes OperationCanceledException, which should be allowed to propagate;
update the catch filter to remove OperationCanceledException so you only catch
DbUpdateException, SqlException and InvalidOperationException, keep the
_logger.LogError call and rethrow a wrapped InvalidOperationException as before,
and apply the same removal of OperationCanceledException from identical catch
filters across other ClinicalScheduler service classes.

In `@web/Areas/ClinicalScheduler/Services/ScheduleEditService.cs`:
- Around line 244-261: In ScheduleEditService.cs update the catch blocks that
currently filter on OperationCanceledException (the try/catch around the DB save
where variables like mothraId, rotationId, weekIds and LogSanitizer are used) so
cancellation is not swallowed: add an explicit catch
(OperationCanceledException) { throw; } immediately before the existing filtered
catch and remove OperationCanceledException from the when(...) filter (so the
filtered catch only checks DbUpdateException, SqlException,
InvalidOperationException); keep the current logging and
InvalidOperationException wrapping logic unchanged in the filtered catch.

In `@web/Areas/ClinicalScheduler/Services/SchedulePermissionService.cs`:
- Around line 91-96: In SchedulePermissionService, do not swallow
OperationCanceledException in the multi-exception catch; add an explicit catch
(OperationCanceledException) { throw; } immediately before the existing catch
(Exception ex) when (ex is DbUpdateException or SqlException or
InvalidOperationException or OperationCanceledException) so cancellation is
rethrown to callers; update all eight methods that currently catch
OperationCanceledException (the blocks that call _userHelper.GetCurrentUser()
and _logger.LogError(...)) to follow this pattern so only genuine DB/operational
exceptions are handled while cancellations propagate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8e04794d-4749-4fa1-abaa-b6b0e8bb36fb

📥 Commits

Reviewing files that changed from the base of the PR and between 38de1ad and aeaf341.

📒 Files selected for processing (8)
  • web/Areas/ClinicalScheduler/Services/InstructorScheduleService.cs
  • web/Areas/ClinicalScheduler/Services/PersonService.cs
  • web/Areas/ClinicalScheduler/Services/RotationService.cs
  • web/Areas/ClinicalScheduler/Services/ScheduleAuditService.cs
  • web/Areas/ClinicalScheduler/Services/ScheduleEditService.cs
  • web/Areas/ClinicalScheduler/Services/SchedulePermissionService.cs
  • web/Areas/ClinicalScheduler/Services/StudentScheduleService.cs
  • web/Areas/ClinicalScheduler/Services/WeekService.cs

rlorenzo added 2 commits May 14, 2026 13:43
CodeQL cs/catch-of-all-exceptions: replace blanket `catch (Exception)`
in 8 ClinicalScheduler service files with a `when` filter restricting
to the exception families these methods actually need to wrap
(DbUpdateException, SqlException, InvalidOperationException,
OperationCanceledException). Anything outside that set now propagates
unchanged.

Four fire-and-forget catches around post-transaction work (email
notifications and audit logging that must not roll back successful DB
changes) are kept intentionally broad and marked with
`#pragma warning disable CA1031` explaining why. Test
EmailNotificationTest.RemoveInstructorScheduleAsync_EmailServiceFails_StillCompletesRemoval
enforces this resilience contract.
ReSharper RedundantNameQualifier flagged all 45 occurrences of
Microsoft.EntityFrameworkCore.DbUpdateException and
Microsoft.Data.SqlClient.SqlException in the when-filtered catches
introduced by codeql/5. Replaced with the unqualified type names;
'using Microsoft.Data.SqlClient;' added to the 7 files that didn't
already have it.
@rlorenzo rlorenzo force-pushed the codeql/5-clinicalscheduler-exceptions branch from aeaf341 to 72211c3 Compare May 14, 2026 20:43
@rlorenzo
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/Areas/ClinicalScheduler/Services/PersonService.cs`:
- Line 212: In PersonService, update the catch filter that currently reads catch
(Exception ex) when (ex is DbUpdateException or SqlException or
InvalidOperationException) to remove the redundant SqlException type (leave
DbUpdateException and InvalidOperationException) so the prior dedicated
SqlException handler remains reachable; modify the catch clause in the
PersonService class accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d5024205-c5f5-434f-b8b7-2dde1f732a9f

📥 Commits

Reviewing files that changed from the base of the PR and between aeaf341 and 72211c3.

📒 Files selected for processing (8)
  • web/Areas/ClinicalScheduler/Services/InstructorScheduleService.cs
  • web/Areas/ClinicalScheduler/Services/PersonService.cs
  • web/Areas/ClinicalScheduler/Services/RotationService.cs
  • web/Areas/ClinicalScheduler/Services/ScheduleAuditService.cs
  • web/Areas/ClinicalScheduler/Services/ScheduleEditService.cs
  • web/Areas/ClinicalScheduler/Services/SchedulePermissionService.cs
  • web/Areas/ClinicalScheduler/Services/StudentScheduleService.cs
  • web/Areas/ClinicalScheduler/Services/WeekService.cs

Comment thread web/Areas/ClinicalScheduler/Services/PersonService.cs Outdated
Two related fixes to the filtered catches added in c269ad7:

1. OperationCanceledException no longer swallowed. The filters caught
   cancellation alongside data-access failures, wrapping it as
   InvalidOperationException (or, in SchedulePermissionService,
   silently returning `false`/empty results). Dropping it from the
   `when` filters lets cooperative cancellation propagate naturally.

2. Unreachable types removed from three filters that listed exceptions
   already handled by a dedicated catch clause above them:
   - PersonService AddRangeInstructorScheduleAsync: SqlException is
     caught at the preceding `catch (SqlException ex)`.
   - ScheduleEditService AddInstructorScheduleAsync and
     RemoveInstructorScheduleAsync: InvalidOperationException is
     rethrown by the preceding `catch (InvalidOperationException)`
     passthrough.
@rlorenzo rlorenzo force-pushed the codeql/5-clinicalscheduler-exceptions branch from 72211c3 to 39e16db Compare May 14, 2026 21:48
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.

2 participants