Skip to content

Improve Not Found error handling#3036

Open
Sabr1n4W wants to merge 17 commits intodevelopfrom
86-improve-notfoundexceptions
Open

Improve Not Found error handling#3036
Sabr1n4W wants to merge 17 commits intodevelopfrom
86-improve-notfoundexceptions

Conversation

@Sabr1n4W
Copy link
Copy Markdown
Contributor

@Sabr1n4W Sabr1n4W commented Apr 9, 2026

Fixes #86
Fixes #1676

Type

  • Bugfix
  • Feature
  • Documentation
  • Refactoring (e.g. Style updates, Test implementation, etc.)
  • Other (please describe):

Checklist

  • Code updated to current develop branch head
  • Passes CI checks
  • Is a part of an issue
  • Tests added for the bugfix or newly implemented feature, describe below why if not
  • Changelog is updated
  • Documentation of code and features exists

Changes

  • Improve 404 error messages
  • Standardize 404 error handling on admin pages
    • On Index pages -> reload list
    • On View pages -> redirect to index page
  • Standardize room not found error handling inside room view
    • Authenticated user -> redirect to room index page
    • Guest -> redirect to 404 error page

Other information

Summary by CodeRabbit

  • New Features

    • Standardized "model not found" API handling with user-facing toast title/details for missing resources
  • Bug Fixes

    • Prevented inappropriate redirects and improved navigation when rooms or related items are deleted or missing
    • Favorites and member/link/file/recording actions now handle missing resources more reliably
  • Improvements

    • UI now shows specific missing-model info (model type and IDs) and emits explicit "not found" events for smoother recovery and list refreshes

@Sabr1n4W Sabr1n4W self-assigned this Apr 9, 2026
@Sabr1n4W Sabr1n4W linked an issue Apr 9, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Warning

Rate limit exceeded

@Sabr1n4W has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 0 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 19 minutes and 0 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a8106db2-5933-476d-8e5f-c77743fd54bf

📥 Commits

Reviewing files that changed from the base of the PR and between 8c048e4 and 256bf30.

📒 Files selected for processing (2)
  • lang/en/app.php
  • resources/js/views/RoomsView.vue

Walkthrough

Adds structured 404 handling: backend returns model_not_found JSON for ModelNotFound cases; routes/controllers and frontend API/component logic now detect this payload to show specialized toasts and perform conditional redirects; extensive unit and e2e tests updated to assert the new behavior.

Changes

Cohort / File(s) Summary
Backend Exception & Translations
app/Exceptions/Handler.php, lang/en/app.php
Handler now detects NotFoundHttpException with a previous ModelNotFoundException and returns { message: "model_not_found", model, ids } for JSON requests; new flash.model_not_found translations and expanded model labels added.
API Routes & Controllers
routes/api.php, app/Http/Controllers/api/v1/RoomMemberController.php, app/Http/Controllers/api/v1/RoomPersonalizedLinkController.php
Route groups reorganized (scopeBindings/middleware grouping); route parameter names changed ({user}{member}, {link}{personalizedLink}) and corresponding controller method parameters updated; some ownership/association abort checks removed.
Frontend API & Constants
resources/js/constants/modelNames.js, resources/js/services/Api.js
New model-name constants added; Api.error() extended to handle model_not_found via handleModelNotFound() which shows a model-not-found toast and conditionally redirects when the model is room.
Room UI Components
resources/js/components/RoomCard.vue, resources/js/components/RoomFavoriteButton.vue, resources/js/components/RoomTabFiles*.vue, resources/js/components/RoomTabMembers*.vue, resources/js/components/RoomTabPersonalizedLinks*.vue, resources/js/components/RoomTabRecordings*.vue
Favorites button gains redirectOnRoomModelNotFound prop; file/member/personalized-link/recording components now inspect error.response.data.model on 404 before triggering component-specific “gone” flows.
Settings/Admin Delete/Action Components
resources/js/components/SettingsRolesDeleteButton.vue, .../SettingsServerPoolsDeleteButton.vue, .../SettingsServersDeleteButton.vue, .../SettingsUsersDeleteButton.vue, .../SettingsUsersResetPasswordButton.vue
Several admin delete/reset components now detect HTTP 404 and emit a notFound event (and close modals) instead of always delegating to generic error handling; some defineEmits updated.
Admin & Room Views
resources/js/views/Admin*.vue, resources/js/views/RoomsView.vue, resources/js/views/RoomsView*.vue
Admin index/view pages listen for not-found events to refresh or navigate to index; room views updated to pass redirectOnRoomModelNotFound: false on some API calls and to conditionally redirect authenticated users to rooms index vs guests to 404 when room model is missing.
Backend Feature Tests
tests/Backend/Feature/api/v1/*.php, tests/Backend/Feature/api/v1/Room/*.php
Many tests extended to assert 404 responses include { message: "model_not_found", model: <...>, ids: [...] } for deleted/missing models across recordings, roles, rooms, files, members, room types, servers, users, etc.
Frontend E2E Tests
tests/Frontend/e2e/**/*.cy.js (numerous admin and rooms specs)
Cypress specs updated to mock 404 responses with the model_not_found payload and to assert the new toast title/details and expected reload/redirect behaviors across a wide range of admin and room interactions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

refactor, tests, frontend

Suggested reviewers

  • samuelwei
  • danielmachill
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Improve Not Found error handling' directly and clearly describes the main objective of the changeset, which is to enhance how 404 errors are handled across the application.
Description check ✅ Passed The description identifies the two linked issues (#86, #1676), categorizes the PR as refactoring, includes a detailed 'Changes' section explaining the improvements, and marks relevant checklist items. The template structure is followed, though some non-critical sections (code updated to develop head, changelog, documentation) are incomplete.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from both linked issues: a custom 404 handler returning structured JSON (#86 [message, model, ids]) and standardized 404 behavior on admin pages (reload on index, redirect on view) and in room views (#1676).
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issues. Backend handler improvements, API route refactoring, frontend error handling refinements, and test updates all address the stated objectives of improving 404 error messages and standardizing error handling across admin and room views.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 86-improve-notfoundexceptions

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.

@cypress
Copy link
Copy Markdown

cypress bot commented Apr 9, 2026

PILOS    Run #2942

Run Properties:  status check passed Passed #2942  •  git commit 256bf30993: Improve Not Found error handling
Project PILOS
Branch Review 86-improve-notfoundexceptions
Run status status check passed Passed #2942
Run duration 07m 27s
Commit git commit 256bf30993: Improve Not Found error handling
Committer Sabrina Wüst
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 623
View all changes introduced in this branch ↗︎

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.77%. Comparing base (84b05fc) to head (256bf30).
⚠️ Report is 4 commits behind head on develop.

Additional details and impacted files
@@              Coverage Diff              @@
##             develop    #3036      +/-   ##
=============================================
+ Coverage      96.70%   96.77%   +0.06%     
- Complexity      1913     1923      +10     
=============================================
  Files            445      458      +13     
  Lines          12915    13070     +155     
  Branches        2078     2123      +45     
=============================================
+ Hits           12490    12648     +158     
+ Misses           425      422       -3     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Sabr1n4W Sabr1n4W marked this pull request as ready for review April 10, 2026 08:01
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: 8

🧹 Nitpick comments (4)
resources/js/views/RoomsView.vue (1)

442-448: Use router.replace() for terminal not-found redirects.

Keeping push() here leaves the broken room URL in browser history, so Back can reopen the same missing-room redirect loop. replace() avoids that while preserving the new auth-vs-guest behavior.

♻️ Proposed change
-            router.push({ name: "rooms.index" });
+            router.replace({ name: "rooms.index" });
...
-            router.push({ name: "404" });
+            router.replace({ name: "404" });

Also applies to: 524-530

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/js/views/RoomsView.vue` around lines 442 - 448, The 404 handling in
RoomsView.vue currently uses router.push which leaves the broken room URL in
history; replace router.push calls with router.replace in the
error.response.status === env.HTTP_NOT_FOUND block (the branch that routes to
"rooms.index" or "404") so the missing-room redirect is terminal, and make the
same change for the second identical block around the other not-found handler
(the other router.push at lines ~524-530) to avoid back-button loops.
tests/Backend/Feature/api/v1/Room/MembershipTest.php (1)

785-791: Consider a shared assertion helper for model_not_found payloads.

These repeated JSON checks are consistent, but a tiny helper (e.g., assertModelNotFound($response, $model, $ids)) would reduce duplication and keep future contract changes centralized.

Also applies to: 801-807, 848-854, 888-894

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Backend/Feature/api/v1/Room/MembershipTest.php` around lines 785 - 791,
Extract the repeated JSON assertions for the "model_not_found" payload into a
single test helper (e.g., assertModelNotFound($response, string $model, array
$ids)) on the base TestCase (or a shared trait) and replace the inline
->assertNotFound()->assertJson([...]) chains in MembershipTest with the new
helper; the helper should accept the response (or perform the status check
itself), assert the 404 via assertNotFound() and assert the JSON body contains
'message' => 'model_not_found', 'model' => $model, and 'ids' => $ids so all
occurrences (the deleteJson route('api.v1.rooms.member.destroy', ...) calls and
similar assertions) use this centralized assertion.
tests/Frontend/e2e/RoomsViewMembers.cy.js (1)

488-505: Register the 404 intercept before reload to avoid a flaky branch.

cy.reload() currently runs before the new roomMembersRequest 404 intercept is installed. If the members request is triggered during reload, this scenario may miss the intended stub and become nondeterministic.

🔧 Suggested stabilization
-    cy.interceptRoomViewRequests();
-    cy.reload();
-
-    // Test 404 error (room not found)
-    cy.interceptRoomIndexRequests();
-    cy.intercept("GET", "api/v1/rooms/abc-def-123/member*", {
+    cy.interceptRoomViewRequests();
+    cy.interceptRoomIndexRequests();
+    // Test 404 error (room not found)
+    cy.intercept("GET", "api/v1/rooms/abc-def-123/member*", {
       statusCode: 404,
       body: {
         message: "model_not_found",
         model: "room",
         ids: ["abc-def-123"],
       },
     }).as("roomMembersRequest");
+    cy.reload();
+    cy.wait("@roomRequest");

     cy.get("#tab-members").click();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Frontend/e2e/RoomsViewMembers.cy.js` around lines 488 - 505, The 404
stub for the members request is installed after cy.reload(), making the test
flaky if the members request fires during reload; move the cy.intercept("GET",
"api/v1/rooms/abc-def-123/member*", { ... }).as("roomMembersRequest") call to
run before cy.reload() (while keeping cy.interceptRoomViewRequests() /
cy.interceptRoomIndexRequests() as needed) so the "@roomMembersRequest" stub is
active during the page reload and the subsequent cy.get("#tab-members").click()
step.
resources/js/services/Api.js (1)

128-135: Prefer replace() for these 404 redirects.

Using push() keeps the stale room URL in history, so Back lands on the broken page and immediately redirects again. replace() avoids that loop and matches the existing unauthenticated/guests-only handlers.

Suggested change
     if (model === ROOM && options.redirectOnRoomModelNotFound !== false) {
       // Redirect to room index page if user is authenticated, otherwise show 404 page, because
       // unauthenticated user is not able to visit the room index page
       if (this.auth.isAuthenticated) {
-        this.router.push({ name: "rooms.index" });
+        this.router.replace({ name: "rooms.index" });
       } else {
-        this.router.push({ name: "404" });
+        this.router.replace({ name: "404" });
       }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/js/services/Api.js` around lines 128 - 135, The redirect logic that
handles model === ROOM when options.redirectOnRoomModelNotFound !== false should
use this.router.replace instead of this.router.push so the stale room URL is not
kept in history; update the branches that check this.auth.isAuthenticated and
call this.router.push({ name: "rooms.index" }) or this.router.push({ name: "404"
}) to call this.router.replace(...) with the same route objects to avoid
back-button redirect loops and match guest/unauthenticated handlers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lang/en/app.php`:
- Around line 69-72: The translation string for flash.model_not_found.details
contains a leading space; open the array entry for
'flash.model_not_found.details' (the 'details' key under 'model_not_found') and
remove the leading whitespace so the value reads 'Model id: :ids' instead of '
Model id: :ids' to ensure consistent UI text formatting.

In `@resources/js/components/SettingsUsersDeleteButton.vue`:
- Around line 109-114: The 404 case is being double-handled: after emitting
notFound in the catch block (when error.response.status === env.HTTP_NOT_FOUND)
the code still calls api.error(error). Modify the catch logic in the delete
flow/handler (the block using modalVisible.value, emit("notFound") and
api.error) so that once you emit("notFound") and set modalVisible.value = false
you skip calling api.error—either by returning early immediately after
emit("notFound") or by wrapping api.error(error) in an else branch that only
runs when the status is not env.HTTP_NOT_FOUND.

In `@tests/Frontend/e2e/RoomsViewDescription.cy.js`:
- Around line 466-483: The test clicks the edit button immediately after
cy.reload(), which can race with the room view request; update the test to wait
for the relevant intercepted room request to finish before reopening the editor
by awaiting the alias created by cy.interceptRoomViewRequests() or
cy.interceptRoomIndexRequests() (e.g., cy.wait('@roomRequest') or the actual
alias returned by those helpers) before calling
cy.get('[data-test="room-description-edit-button"]').click() so the reload
completes and the 404 save path is exercised reliably.

In `@tests/Frontend/e2e/RoomsViewGeneral.cy.js`:
- Line 727: Fix the typo in the inline comment that currently reads "// Check
wit 404 error (room not found) as guest" by changing "wit" to "with" so it reads
"// Check with 404 error (room not found) as guest"; this update should be made
in the RoomsViewGeneral.cy.js test comment near the guest 404 check.

In `@tests/Frontend/e2e/RoomsViewMembersBulkActions.cy.js`:
- Around line 412-422: The test races selecting members with the members grid
reload; after calling cy.interceptRoomViewRequests() and after clicking
'#tab-members' (or immediately after cy.reload()/reopening the tab), wait for
the intercepted members request alias '@roomMembersRequest' before interacting
with '[data-test="room-members-select-all-checkbox"]'; update the three
occurrences (around cy.interceptRoomViewRequests(), cy.reload(),
cy.get("#tab-members").click(), and the select-all click) to include a
cy.wait("@roomMembersRequest") so the members grid is fully loaded before
selection.

In `@tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js`:
- Around line 970-1001: After reloading and clicking the recordings tab you must
wait for the refreshed recordings request before opening the delete dialog to
avoid the race; after invoking cy.reload() and/or after
cy.get("#tab-recordings").click(), add a cy.wait for the recordings intercept
alias (the `@roomRecordingsRequest` created by cy.interceptRoomRecordingsRequests)
and only then proceed to find and click the delete button and confirm the dialog
so the subsequent 404 assertion targets the refreshed list.

In `@tests/Frontend/e2e/RoomsViewSettings.cy.js`:
- Around line 2372-2378: The current cy.url().should("include", "/rooms") can
match both /rooms and /rooms/abc-def-123; replace it with a stricter assertion
that the pathname is exactly the rooms index (e.g. use
cy.location('pathname').should('match', /^\/rooms\/?$/) or assert equality to
'/rooms') so the test fails if the browser is still on the room detail URL;
update the assertion that currently calls cy.url() to use
cy.location('pathname') and the regexp or exact equals check, leaving the
cy.checkToastMessage call unchanged.
- Around line 1691-1702: The PUT intercept created with cy.intercept("PUT",
"api/v1/rooms/abc-def-123", ...) is missing the alias expected by the later
cy.wait("@roomSettingsSaveRequest"); update the intercept call to attach the
alias by calling .as("roomSettingsSaveRequest") so the cy.wait can find the
request; you should modify the cy.intercept invocation in
RoomsViewSettings.cy.js to include .as("roomSettingsSaveRequest") to match the
existing cy.wait.

---

Nitpick comments:
In `@resources/js/services/Api.js`:
- Around line 128-135: The redirect logic that handles model === ROOM when
options.redirectOnRoomModelNotFound !== false should use this.router.replace
instead of this.router.push so the stale room URL is not kept in history; update
the branches that check this.auth.isAuthenticated and call this.router.push({
name: "rooms.index" }) or this.router.push({ name: "404" }) to call
this.router.replace(...) with the same route objects to avoid back-button
redirect loops and match guest/unauthenticated handlers.

In `@resources/js/views/RoomsView.vue`:
- Around line 442-448: The 404 handling in RoomsView.vue currently uses
router.push which leaves the broken room URL in history; replace router.push
calls with router.replace in the error.response.status === env.HTTP_NOT_FOUND
block (the branch that routes to "rooms.index" or "404") so the missing-room
redirect is terminal, and make the same change for the second identical block
around the other not-found handler (the other router.push at lines ~524-530) to
avoid back-button loops.

In `@tests/Backend/Feature/api/v1/Room/MembershipTest.php`:
- Around line 785-791: Extract the repeated JSON assertions for the
"model_not_found" payload into a single test helper (e.g.,
assertModelNotFound($response, string $model, array $ids)) on the base TestCase
(or a shared trait) and replace the inline ->assertNotFound()->assertJson([...])
chains in MembershipTest with the new helper; the helper should accept the
response (or perform the status check itself), assert the 404 via
assertNotFound() and assert the JSON body contains 'message' =>
'model_not_found', 'model' => $model, and 'ids' => $ids so all occurrences (the
deleteJson route('api.v1.rooms.member.destroy', ...) calls and similar
assertions) use this centralized assertion.

In `@tests/Frontend/e2e/RoomsViewMembers.cy.js`:
- Around line 488-505: The 404 stub for the members request is installed after
cy.reload(), making the test flaky if the members request fires during reload;
move the cy.intercept("GET", "api/v1/rooms/abc-def-123/member*", { ...
}).as("roomMembersRequest") call to run before cy.reload() (while keeping
cy.interceptRoomViewRequests() / cy.interceptRoomIndexRequests() as needed) so
the "@roomMembersRequest" stub is active during the page reload and the
subsequent cy.get("#tab-members").click() step.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 571f1968-c615-451e-b8a8-5e3dc5fa57af

📥 Commits

Reviewing files that changed from the base of the PR and between 84b05fc and fb9b10a.

📒 Files selected for processing (85)
  • app/Exceptions/Handler.php
  • app/Http/Controllers/api/v1/RoomMemberController.php
  • app/Http/Controllers/api/v1/RoomPersonalizedLinkController.php
  • lang/en/app.php
  • resources/js/components/RoomCard.vue
  • resources/js/components/RoomFavoriteButton.vue
  • resources/js/components/RoomTabFilesDeleteButton.vue
  • resources/js/components/RoomTabFilesEditButton.vue
  • resources/js/components/RoomTabMembersDeleteButton.vue
  • resources/js/components/RoomTabMembersEditButton.vue
  • resources/js/components/RoomTabPersonalizedLinksDeleteButton.vue
  • resources/js/components/RoomTabPersonalizedLinksEditButton.vue
  • resources/js/components/RoomTabRecordingsDeleteButton.vue
  • resources/js/components/RoomTabRecordingsEditButton.vue
  • resources/js/components/SettingsRolesDeleteButton.vue
  • resources/js/components/SettingsServerPoolsDeleteButton.vue
  • resources/js/components/SettingsServersDeleteButton.vue
  • resources/js/components/SettingsUsersDeleteButton.vue
  • resources/js/components/SettingsUsersResetPasswordButton.vue
  • resources/js/constants/modelNames.js
  • resources/js/services/Api.js
  • resources/js/views/AdminRolesIndex.vue
  • resources/js/views/AdminRolesView.vue
  • resources/js/views/AdminRoomTypesView.vue
  • resources/js/views/AdminServerPoolsIndex.vue
  • resources/js/views/AdminServerPoolsView.vue
  • resources/js/views/AdminServersIndex.vue
  • resources/js/views/AdminServersView.vue
  • resources/js/views/AdminUsersIndex.vue
  • resources/js/views/AdminUsersView.vue
  • resources/js/views/RoomsView.vue
  • routes/api.php
  • tests/Backend/Feature/api/v1/RecordingTest.php
  • tests/Backend/Feature/api/v1/RoleTest.php
  • tests/Backend/Feature/api/v1/Room/FileTest.php
  • tests/Backend/Feature/api/v1/Room/MembershipTest.php
  • tests/Backend/Feature/api/v1/Room/RoomDescriptionTest.php
  • tests/Backend/Feature/api/v1/Room/RoomPersonalizedLinkTest.php
  • tests/Backend/Feature/api/v1/Room/RoomTest.php
  • tests/Backend/Feature/api/v1/RoomTypeTest.php
  • tests/Backend/Feature/api/v1/ServerPoolTest.php
  • tests/Backend/Feature/api/v1/ServerTest.php
  • tests/Backend/Feature/api/v1/UserTest.php
  • tests/Frontend/e2e/AdminRolesEdit.cy.js
  • tests/Frontend/e2e/AdminRolesIndexRoleActions.cy.js
  • tests/Frontend/e2e/AdminRolesView.cy.js
  • tests/Frontend/e2e/AdminRolesViewRoleActions.cy.js
  • tests/Frontend/e2e/AdminRoomTypesEdit.cy.js
  • tests/Frontend/e2e/AdminRoomTypesIndexRoomTypeActions.cy.js
  • tests/Frontend/e2e/AdminRoomTypesView.cy.js
  • tests/Frontend/e2e/AdminRoomTypesViewRoomTypeActions.cy.js
  • tests/Frontend/e2e/AdminServerPoolsEdit.cy.js
  • tests/Frontend/e2e/AdminServerPoolsIndexServerPoolActions.cy.js
  • tests/Frontend/e2e/AdminServerPoolsView.cy.js
  • tests/Frontend/e2e/AdminServerPoolsViewServerPoolActions.cy.js
  • tests/Frontend/e2e/AdminServersEdit.cy.js
  • tests/Frontend/e2e/AdminServersIndexServerActions.cy.js
  • tests/Frontend/e2e/AdminServersView.cy.js
  • tests/Frontend/e2e/AdminServersViewServerActions.cy.js
  • tests/Frontend/e2e/AdminUsersEdit.cy.js
  • tests/Frontend/e2e/AdminUsersEditBase.cy.js
  • tests/Frontend/e2e/AdminUsersEditEmail.cy.js
  • tests/Frontend/e2e/AdminUsersEditOthers.cy.js
  • tests/Frontend/e2e/AdminUsersEditSecurity.cy.js
  • tests/Frontend/e2e/AdminUsersIndexUserActions.cy.js
  • tests/Frontend/e2e/AdminUsersView.cy.js
  • tests/Frontend/e2e/AdminUsersViewUserActions.cy.js
  • tests/Frontend/e2e/RoomsIndex.cy.js
  • tests/Frontend/e2e/RoomsViewDescription.cy.js
  • tests/Frontend/e2e/RoomsViewFiles.cy.js
  • tests/Frontend/e2e/RoomsViewFilesFileActions.cy.js
  • tests/Frontend/e2e/RoomsViewGeneral.cy.js
  • tests/Frontend/e2e/RoomsViewHistory.cy.js
  • tests/Frontend/e2e/RoomsViewHistoryMeetingActions.cy.js
  • tests/Frontend/e2e/RoomsViewMeetings.cy.js
  • tests/Frontend/e2e/RoomsViewMembers.cy.js
  • tests/Frontend/e2e/RoomsViewMembersBulkActions.cy.js
  • tests/Frontend/e2e/RoomsViewMembersMemberActions.cy.js
  • tests/Frontend/e2e/RoomsViewPersonalizedLinks.cy.js
  • tests/Frontend/e2e/RoomsViewPersonalizedLinksTokenActions.cy.js
  • tests/Frontend/e2e/RoomsViewRecordings.cy.js
  • tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js
  • tests/Frontend/e2e/RoomsViewSettings.cy.js
  • tests/Frontend/e2e/RoomsViewStreaming.cy.js
  • tests/Frontend/e2e/RoomsViewStreamingConfigActions.cy.js

Comment on lines +109 to 114
if (error.response && error.response.status === env.HTTP_NOT_FOUND) {
modalVisible.value = false;
emit("notFound");
}
api.error(error);
})
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Apr 10, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Stop double-handling 404 after emitting notFound.

On Line 109 onward, the code emits notFound but still calls api.error(error), which can cause duplicate/conflicting handling for the same response.

🔧 Proposed fix
 import { ref } from "vue";
 import { useApi } from "../composables/useApi.js";
 import env from "../env.js";
+import { USER } from "../constants/modelNames.js";
@@
-      if (error.response && error.response.status === env.HTTP_NOT_FOUND) {
+      if (
+        error.response?.status === env.HTTP_NOT_FOUND &&
+        error.response.data?.model === USER
+      ) {
         modalVisible.value = false;
         emit("notFound");
+        return;
       }
       api.error(error);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (error.response && error.response.status === env.HTTP_NOT_FOUND) {
modalVisible.value = false;
emit("notFound");
}
api.error(error);
})
if (
error.response?.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === USER
) {
modalVisible.value = false;
emit("notFound");
return;
}
api.error(error);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/js/components/SettingsUsersDeleteButton.vue` around lines 109 -
114, The 404 case is being double-handled: after emitting notFound in the catch
block (when error.response.status === env.HTTP_NOT_FOUND) the code still calls
api.error(error). Modify the catch logic in the delete flow/handler (the block
using modalVisible.value, emit("notFound") and api.error) so that once you
emit("notFound") and set modalVisible.value = false you skip calling
api.error—either by returning early immediately after emit("notFound") or by
wrapping api.error(error) in an else branch that only runs when the status is
not env.HTTP_NOT_FOUND.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The notFound emit is used to inform the parent component that either the user list needs to be reloaded or a redirect to the user index page is required.

The api.error(error) call is responsible for displaying a toast message to inform the user that an error occurred.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Sabr1n4W, thanks for the clarification — that makes the separation of concerns clear. The notFound emit handles the structural/navigation response in the parent, while api.error takes care of surfacing the toast notification independently. That's a reasonable design.

One minor thing still worth considering: the current condition fires emit("notFound") for any HTTP 404, not just when data.model === "user". If a 404 with a different model (e.g., from a race condition or an unrelated resource) were ever returned by this endpoint, it would still trigger the parent's reload/redirect. Adding the model discriminator (as suggested in the original fix) would make the intent more explicit and guard against that edge case — but I understand if you consider that overly defensive for this endpoint.

(o´ω`o)


✏️ Learnings added
Learnt from: Sabr1n4W
Repo: THM-Health/PILOS PR: 3036
File: resources/js/components/SettingsUsersDeleteButton.vue:109-114
Timestamp: 2026-04-10T14:09:05.618Z
Learning: In `resources/js/components/SettingsUsersDeleteButton.vue` (and similar Settings*DeleteButton components in the PILOS project), `emit("notFound")` and `api.error(error)` are intentionally called together in the 404 catch block. They serve different purposes: `emit("notFound")` informs the parent component to reload the list or redirect to the index page, while `api.error(error)` is responsible for displaying the toast message to the user. Both should run for a 404 — no early return is needed.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

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.

404 error handling on admin pages Improve NotFoundExceptions

1 participant