Skip to content

fix(editor): Gracefully handle expired confirmations in setup wizard#28471

Open
aalises wants to merge 2 commits intomasterfrom
aalises/fix-setup-wizard-expired-confirmation
Open

fix(editor): Gracefully handle expired confirmations in setup wizard#28471
aalises wants to merge 2 commits intomasterfrom
aalises/fix-setup-wizard-expired-confirmation

Conversation

@aalises
Copy link
Copy Markdown
Contributor

@aalises aalises commented Apr 14, 2026

Summary

When the Instance AI setup wizard has been open for >10 minutes, the backend sweeps the suspended run (confirmation timeout). If the user then clicks "Later" or "Continue":

  • Before: POST returns 404 → confirmAction() catches it generically → shows cryptic "Confirmation failed" toast → UI stays permanently stuck (buttons do nothing)
  • After: 404 is detected as "run already gone" → wizard gracefully dismisses with "Deferred" status → no stuck UI

What changed

  • confirmAction() in instanceAi.store.ts: Detect ResponseError with httpStatusCode === 404 and return true instead of false (suppressing the error toast). This lets callers proceed with their cleanup path. Uses the same error instanceof ResponseError pattern already established in sendMessage() (line 787).
  • New isConfirmationGone() helper: Checks if a tool call's isLoading is false (meaning the run was already cancelled via the SSE run-finish event). This prevents handleApply() and handleTestTrigger() from waiting 60 seconds for a tool result that will never arrive from a cancelled run.
  • Guards in useSetupActions.ts: handleApply() and handleTestTrigger() check isConfirmationGone() after confirmAction returns true, and gracefully dismiss instead of entering the dead wait.
  • Guard in InstanceAiCredentialSetup.vue: handleContinue() checks isConfirmationGone() and shows "Deferred" state instead of "Approved" when the run was swept.

Why this works

The backend already sends a run-finish SSE event with status: 'cancelled' when it sweeps an expired run. The frontend reducer sets tc.isLoading = false, which removes the confirmation from the panel. The bug only occurs in the race where the user clicks before the SSE event arrives — this fix handles that race by treating the 404 as "effectively handled."

Linear tickets

When the backend sweeps a suspended run after the 10-minute timeout,
clicking "Later" or "Continue" on the setup wizard would POST to a
404 endpoint, show a cryptic "Confirmation failed" toast, and leave
the UI permanently stuck.

- Detect 404 in confirmAction() and return true (run is already gone,
  let callers proceed with cleanup instead of getting stuck)
- Add isConfirmationGone() helper to check if a tool call's run was
  already cancelled via SSE
- Guard handleApply/handleTestTrigger against waiting 60s for a tool
  result that will never arrive from a cancelled run
- Guard credential setup handleContinue to show deferred state when
  the run was swept
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 14, 2026

Bundle Report

Changes will increase total bundle size by 695 bytes (0.0%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
editor-ui-esm 45.75MB 695 bytes (0.0%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: editor-ui-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/worker-*.js -3.15MB 17.9kB -99.43%
assets/worker-*.js 3.15MB 3.17MB 17586.03% ⚠️
assets/InstanceAiView-*.js 598 bytes 344.05kB 0.17%
assets/CreditWarningBanner-*.js 97 bytes 55.31kB 0.18%

Files in assets/InstanceAiView-*.js:

  • ./src/features/ai/instanceAi/composables/useSetupActions.ts → Total Size: 7.45kB

  • ./src/features/ai/instanceAi/components/InstanceAiCredentialSetup.vue → Total Size: 394 bytes

Files in assets/CreditWarningBanner-*.js:

  • ./src/features/ai/instanceAi/instanceAi.store.ts → Total Size: 24.46kB

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 14, 2026

Codecov Report

❌ Patch coverage is 9.09091% with 20 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...tures/ai/instanceAi/composables/useSetupActions.ts 6.66% 14 Missing ⚠️
...nstanceAi/components/InstanceAiCredentialSetup.vue 20.00% 4 Missing ⚠️
...-ui/src/features/ai/instanceAi/instanceAi.store.ts 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@aalises aalises requested a review from scdekov April 14, 2026 11:54
@aalises aalises marked this pull request as ready for review April 14, 2026 11:55
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiCredentialSetup.vue">

<violation number="1" location="packages/frontend/editor-ui/src/features/ai/instanceAi/components/InstanceAiCredentialSetup.vue:284">
P2: `handleContinue()` can still resolve expired confirmations as `approved` when 404 is returned before SSE updates local `isLoading` state.</violation>
</file>

<file name="packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useSetupActions.ts">

<violation number="1" location="packages/frontend/editor-ui/src/features/ai/instanceAi/composables/useSetupActions.ts:193">
P2: The new `isConfirmationGone()` early return can misclassify completed confirmations as deferred, causing successful apply/test-trigger results to be dropped.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant User
    participant UI as Setup Wizard (Vue)
    participant Logic as useSetupActions (Composable)
    participant Store as Instance AI Store
    participant API as Backend API
    participant SSE as SSE Stream

    Note over User, API: Scenario: User acts on a Confirmation that has expired (>10m)

    User->>UI: Click "Continue" or "Later"
    UI->>Store: confirmAction(requestId, credentials)
    
    Store->>API: POST /confirm
    
    alt Run Expired (Timeout)
        API-->>Store: 404 Not Found
        Store->>Store: CHANGED: Catch 404 and return true (suppress error toast)
    else Success
        API-->>Store: 200 OK
    end
    
    Store-->>UI: return success (true)

    Note over UI, Logic: Post-confirmation flow handles potential "Race Condition"

    UI->>Store: NEW: isConfirmationGone(requestId)
    Store->>Store: Check tool call isLoading state
    
    opt SSE Update (Concurrent)
        SSE->>Store: event: run-finish (status: cancelled)
        Store->>Store: Set toolCall.isLoading = false
    end

    Store-->>UI: return isGone (true)

    alt NEW: Run was swept/gone
        UI->>UI: Set state to "Deferred"
        UI->>Store: resolveConfirmation(requestId, 'deferred')
        Note right of UI: UI dismisses gracefully instead of sticking
    else Run still active
        UI->>Store: resolveConfirmation(requestId, 'approved')
        
        Logic->>Store: NEW: isConfirmationGone(requestId)
        alt Run not gone
            Logic->>Logic: waitForToolResult()
        else Run gone during wait
            Logic->>Logic: Graceful exit (skip 60s timeout)
        end
    end

    Note over UI, API: Final state: Wizard closes without "Confirmation failed" error
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

… store state

Replace racy isConfirmationGone() store check with a direct 'expired'
return value from confirmAction(), eliminating the race condition between
API response and SSE state updates.
@github-actions
Copy link
Copy Markdown
Contributor

Performance Comparison

Comparing currentlatest master14-day baseline

docker-stats

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
docker-image-size-runners 393.00 MB 393.00 MB 391.63 MB (σ 11.06) +0.0% +0.3%
docker-image-size-n8n 1269.76 MB 1269.76 MB 1269.76 MB (σ 0.00) +0.0% +0.0%

Memory consumption baseline with starter plan resources

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
memory-heap-used-baseline 114.76 MB 114.05 MB 113.86 MB (σ 0.84) +0.6% +0.8% ⚠️
memory-rss-baseline 227.13 MB 287.98 MB 284.98 MB (σ 42.51) -21.1% -20.3% ⚠️

Idle baseline with Instance AI module loaded

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
instance-ai-heap-used-baseline 187.24 MB 186.52 MB 186.34 MB (σ 0.24) +0.4% +0.5% 🔴
instance-ai-rss-baseline 349.73 MB 388.20 MB 372.63 MB (σ 22.95) -9.9% -6.1%
How to read this table
  • Current: This PR's value (or latest master if PR perf tests haven't run)
  • Latest Master: Most recent nightly master measurement
  • Baseline: Rolling 14-day average from master
  • vs Master: PR impact (current vs latest master)
  • vs Baseline: Drift from baseline (current vs rolling avg)
  • Status: ✅ within 1σ | ⚠️ 1-2σ | 🔴 >2σ regression

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.

1 participant