bug: deleting a user in the admin panel does nothing #277
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Description
Clicking "Löschen…" on a user's detail page in the admin panel appears to do nothing. Investigation found two concrete backend bugs that block deletion, plus a migration bug that would prevent fresh-database startup.
Root cause analysis
1.
invite_tokens.created_by— missing ON DELETE clause (most likely cause of silent failure)File:
backend/src/main/resources/db/migration/V45__add_invite_tokens.sqlNo
ON DELETEclause defaults toRESTRICT. If the user being deleted has ever created an invite token, Postgres will reject theDELETEwith a foreign key violation. Spring Boot returns a raw 500 (not aDomainException), so the frontend shows a generic "Interner Fehler" banner inside the scrollable form body — easy to miss. The fix isON DELETE SET NULL(keeping invite history, just losing attribution) orON DELETE CASCADE(deleting their invites too).2. V46 migration references a non-existent table
File:
backend/src/main/resources/db/migration/V46__add_audit_log.sqlThe
AppUserentity is mapped to@Table(name = "users"), notapp_users. This FK reference is wrong and would cause Flyway to fail on any fresh database initialization, preventing app startup entirely. Should be:Fix checklist
V47__fix_invite_tokens_cascade.sqlthat changesinvite_tokens.created_bytoON DELETE SET NULL(or CASCADE — decide first)V46__add_audit_log.sql:app_users(id)→users(id)(requires resetting/recreating the DB if already applied, or adding a corrective migration V47 if the column exists)Updated analysis
The
invite_tokensconstraint is not the cause — the user being deleted only accepted an invite, not created one. And there was no error banner, so the server action either didn't run or the backend succeeded but the navigation was silently blocked.Likely root cause:
beforeNavigateinuseUnsavedWarningcancels the post-delete redirectfrontend/src/lib/hooks/useUnsavedWarning.svelte.ts:isDirtybecomestruethe moment anyinputevent fires on the edit form (oninput={unsaved.markDirty}). This can happen without the user deliberately typing — browser autofill (e.g., password field) is enough.Flow when
isDirtyis true at delete time:redirect(303, '/admin/users')enhancetries to navigatebeforeNavigatefires, seesisDirty = true, cancels navigationshowUnsavedWarning = true— an "unsaved changes" banner appears in the scrollable form body/admin/users/[id], list doesn't updateThe user sees: confirm dialog disappeared, page unchanged → "nothing happened". The unsaved-changes banner is not an error banner, so easy to overlook. Meanwhile the user has already been deleted.
Fix
Clear
isDirtybefore submitting the delete form in+page.svelte:clearOnSuccess()already setsisDirty = falseandshowUnsavedWarning = false, so this reuses the existing API.The two migration bugs from the original report (V46 wrong FK table name, invite_tokens missing ON DELETE) are separate issues and still need fixing — but they are not the cause of this "nothing happens" symptom.
Further update
The user is still present after a full page refresh → the backend DELETE was never executed. This rules out the
beforeNavigatetheory too.Actual root cause: wrong usage of the confirm service with
use:enhanceThe current approach in
+page.svelte:The problem:
requestSubmit()fires asubmitevent that SvelteKit'senhanceshould intercept — but this indirect, async-separated approach is fragile. It's not the pattern the confirm service was designed for. Theconfirm.svelte.tsmodule itself documents the correct pattern in its JSDoc (lines 22–27):Fix
Use a real
type="submit"button and move the confirm guard into theenhancecallback:No
bind:this, norequestSubmit(), no extra event handler needed. Theenhancecallback awaits the dialog, cancels if rejected, otherwise lets the form submit proceed normally. Theunsaved.clearOnSuccess()call ensuresbeforeNavigatedoesn't cancel the post-delete redirect if the user happened to touch the edit form.