fix(frontend): prevent API tokens page crash on delete modal close - #3417
fix(frontend): prevent API tokens page crash on delete modal close#3417Marfa wants to merge 10 commits into
Conversation
Clearing tokenToDelete while Modal still renders its slot caused a null title read and a follow-up Vue emitsOptions crash on the API tokens page. Co-authored-by: Cursor <cursoragent@cursor.com>
Test plan results (live instance)Verified against https://todo.themarfa.name with patched SPA (
Deploy note: frontend overlay at |
CI failuresBoth failing checks look unrelated to this PR’s changes:
Happy to re-trigger once those are fixed on the repo side. Local unit tests and a live create/delete flow on a patched instance are green (see earlier comment). |
tink-bot
left a comment
There was a problem hiding this comment.
🤖 PR Swarm — automated review, round 1. See inline comments.
|
🤖 PR Swarm — automated review. Panel: bughunter · security · conventions · design · verifier — round 3 (final) @ Verdict: ✅ APPROVE — 2 questions open for maintainer3 rounds done. All actionable findings fixed, runtime-verified. Diff shrank from author's +262/−9 to +177/−44 while gaining real regression coverage. Two ❓ threads await your reply — answer in-thread, re-run swarm to implement. ❓ Needs your input (2)
✅ Fixed round 3 (2)
💤 Resolved as nit, round 3 (4)
🔎 Runtime verification (round 3 — delete flow re-drive)Full sentences for the record:
Reviewer summaries (round 3)
Previous rounds (2)round 1 @ |
tink-bot
left a comment
There was a problem hiding this comment.
🤖 PR Swarm — automated review, round 2. See inline comments.
tink-bot
left a comment
There was a problem hiding this comment.
🤖 PR Swarm — automated review, round 3 (final). See inline comments.
| return | ||
| } | ||
| tokens.value.splice(index, 1) | ||
| tokenToDelete.value = undefined |
There was a problem hiding this comment.
[convergent: design + bughunter] 🟡 MEDIUM — ❓ needs your input
Clear-before-await trade-off, panel split, round cap hit — your call. Current shape (ships now): ref cleared + modal closed before await service.delete; double-submit dead (verified 8/8 runtime trials, exactly one DELETE incl. two synchronous clicks in one JS task); token name blanks in fading modal for ~150ms but measured imperceptible (opacity 0.075 at 60ms). Cost vs main: on DELETE rejection modal is already dismissed (main kept it open for retry); error still surfaces via global errorHandler, row stays. Alternative both reviewers converged on: keep tokenToDelete set, separate const deleting = ref(false) guard — keeps modal open on failure, kills flash, ?. still needed for close-fade. Same hole also exists pre-existing in BotUsers.vue:108 (two clicks = two DELETEs there, no crash). Options: (a) keep current shape, (b) swarm applies deleting-flag shape here, (c) follow-up PR covering ApiTokens + BotUsers uniformly. Reply here; re-run swarm to implement.
There was a problem hiding this comment.
(a) keep the current shape for this PR.
Double-submit is verified dead, the blank-name flash is imperceptible, and DELETE failures still surface via the global error handler with the row intact. A deleting flag / unifying with BotUsers.vue is a reasonable follow-up if maintainers want that pattern everywhere — I’d rather not reshape this PR again for it.
|
|
||
| // altInput (or mobileInput on the mobile path) is a fresh element inheriting no attributes from the | ||
| // input we render, so label it here | ||
| const labelDateInput: Hook = (_dates, _str, instance) => { |
There was a problem hiding this comment.
[verifier] 🟡 MEDIUM — ❓ needs your input (anchored nearest — affected markup not in diff)
Pre-existing crash on this page, reproduces on main identically: switch Expires "Custom" → preset throws Vue insertBefore NotFoundError (flatpickr mutates DOM owned by v-if), corrupts vnode tree; next create submit throws, then emitsOptions — the very error class this PR's title targets. Root cause separate from this PR (flatpickr DOM ownership); candidate fix: stable-keyed wrapper element around <flat-pickr> or CSS-hide instead of v-if. Fix in this PR or file follow-up issue? Reply here; re-run swarm to implement.
There was a problem hiding this comment.
Follow-up issue, please — leave it out of this PR.
Agreed it’s a real crash class on main (flatpickr mutating v-if-owned DOM), but it’s separate from the delete-modal race this PR targets. Happy to open/track a dedicated issue if that helps.

Summary
tokenToDeleteuntil the delete modal finishes closing, and guard the modal slot withv-if=tokenToDelete. Clearing the ref immediately raced with Modal's ~150ms close animation and threwCannot read properties of null (reading 'title'), which then surfaced as Vue's follow-upemitsOptionserror on later updates.useFlatpickrLanguage()once at setup inApiTokenForminstead of inside a computed getter.Test plan
pnpm exec vitest run src/views/user/settings/ApiTokens.test.ts src/components/token/ApiTokenForm.test.ts/user/settings/api-tokens, create a token, then delete it and confirm no error toastemitsOptionserrors