From 45a104a15cb5015ba92b46c208a52e48aed33e38 Mon Sep 17 00:00:00 2001 From: Sandra Zollner Date: Thu, 27 Aug 2026 07:18:34 +0000 Subject: [PATCH] fix(editor): Show error when eligible reviewers fail to load (#37089) Co-authored-by: Claude Opus 4.8 (1M context) --- .../frontend/@n8n/i18n/src/locales/en.json | 1 + .../WorkflowSubmitForReviewDialog.test.ts | 24 ++++++++++++++++--- .../WorkflowSubmitForReviewDialog.vue | 9 +++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index 38dff4ec195..fcbd1ab43f1 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -4715,6 +4715,7 @@ "workflowReviews.submitForReview.submit": "Submit", "workflowReviews.submitForReview.error.title": "Workflow couldn't be submitted for review", "workflowReviews.submitForReview.error.save": "Save the workflow and try again.", + "workflowReviews.submitForReview.error.loadReviewers": "Couldn't load reviewers", "workflowReviews.updateReview.title": "Submit latest changes to existing review", "workflowReviews.updateReview.description": "This workflow already has an {review}. Submit latest changes to the existing review, to get them approved.", "workflowReviews.updateReview.description.review": "open review", diff --git a/packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.test.ts b/packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.test.ts index b8e97c452da..15f9bbc570e 100644 --- a/packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.test.ts +++ b/packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.test.ts @@ -487,15 +487,33 @@ describe('WorkflowSubmitForReviewDialog', () => { expect(getByTestId('workflow-review-submit-button')).toBeEnabled(); }); - it('keeps submission blocked when loading the reviewers fails', async () => { - vi.mocked(fetchEligibleReviewers).mockRejectedValue(new Error('nope')); + it('shows error toast and keeps submission blocked when loading the reviewers fails', async () => { + const error = new Error('nope'); + vi.mocked(fetchEligibleReviewers).mockRejectedValue(error); const { getByTestId, goToStep2 } = await renderDialog(); - await goToStep2(); + await waitFor(() => expect(mockShowError).toHaveBeenCalledWith(error, expect.any(String))); + + await goToStep2(); await userEvent.type(getByTestId('workflow-review-title-input'), 'Review payments'); expect(getByTestId('workflow-review-submit-button')).toBeDisabled(); expect(createWorkflowReviewRequest).not.toHaveBeenCalled(); + }); + + it('stays silent when the reviewer load fails after the dialog has closed', async () => { + let rejectLoad!: (error: unknown) => void; + const pendingLoad = new Promise((_resolve, reject) => { + rejectLoad = reject; + }); + vi.mocked(fetchEligibleReviewers).mockReturnValue(pendingLoad); + const { rerender, flushSave } = await renderDialog(); + + // Close before the in-flight load settles, then let it fail. + await rerender({ open: false, workflowId: 'workflow-1', flushSave }); + rejectLoad(new Error('nope')); + await pendingLoad.catch(() => {}); + expect(mockShowError).not.toHaveBeenCalled(); }); diff --git a/packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.vue b/packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.vue index 4906bf3f6ea..a3c31658341 100644 --- a/packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.vue +++ b/packages/frontend/editor-ui/src/features/workflow-reviews/components/WorkflowSubmitForReviewDialog.vue @@ -101,9 +101,10 @@ const loadEligibleReviewers = async () => { }); if (sequence !== loadReviewersSequence) return; eligibleReviewers.value = data; - } catch { + } catch (error) { if (sequence !== loadReviewersSequence) return; eligibleReviewers.value = []; + toast.showError(error, i18n.baseText('workflowReviews.submitForReview.error.loadReviewers')); } finally { if (sequence === loadReviewersSequence) isLoadingReviewers.value = false; } @@ -112,7 +113,11 @@ const loadEligibleReviewers = async () => { watch( () => props.open, (isOpen) => { - if (!isOpen) return; + if (!isOpen) { + // Invalidate any in-flight load so it can't toast after the dialog closes. + loadReviewersSequence++; + return; + } step.value = 1; reviewTitle.value = '';