Skip to content

fix(frontend): stop stale flows-list refetch from bouncing New Flow back to the list - #14288

Merged
erichare merged 3 commits into
release-1.11.2from
fix/new-flow-stale-store-bounce
Jul 30, 2026
Merged

fix(frontend): stop stale flows-list refetch from bouncing New Flow back to the list#14288
erichare merged 3 commits into
release-1.11.2from
fix/new-flow-stale-store-bounce

Conversation

@erichare

@erichare erichare commented Jul 27, 2026

Copy link
Copy Markdown
Member

Summary

Fixes LE-2019 (found by the E2E regression suite, oriontech-me/langflow-e2e#966): clicking New Flow immediately after returning to the flows list via the header back chevron silently does nothing — no navigation, no welcome overlay, no templates modal, no toast, no console error — and leaves an orphan empty flow behind. Affects 1.10.1+ (create-then-navigate introduced by #12575); the failing clicks correlate exactly with "zero flow cards painted yet".

Root cause (reproduced deterministically)

  1. Back-navigation to /flows remounts the app-header FlowMenu, which refetches GET /api/v1/flows/?get_all=true&header_flows=true (staleTime 0). Its response is a pre-creation snapshot.
  2. The click runs useStartNewFlow: POST /api/v1/flows/ → 201 → setFlows([created, …])navigate("/flow/<id>"). FlowPage mounts and starts loading the flow; currentFlowId stays "" until the canvas GET resolves.
  3. The stale list response lands now. React Query has already superseded that fetch (usePostAddFlow.onSettledrefetchQueries), but the queryFn's api.get carries no AbortSignal, so the "cancelled" fetch keeps running and its setFlows(stale) side effect rewrites the store without the new flow.
  4. FlowPage's existence guard (flows.find(id) miss while currentFlowId === "") silently navigate("/all") — the user is bounced back to the list with nothing to show. Captured URL trail: /flows → /flow/<id> → /all.

The "cards: 0" correlation from the bug report is exactly this window: cards not painted ⇔ the back-nav queries are still in flight ⇔ a stale response is guaranteed to land after the navigation.

Fix (two independent layers)

  • use-get-refresh-flows-query.ts — forward the query's AbortSignal into both api.get calls, so a superseded fetch (or one whose observers unmounted) aborts for real and never reaches setFlows. Cancellations are also excluded from the "could not load flows" error toast (axios.isCancel).
  • FlowPage/index.tsx — when the route's flow id is missing from the (mutable, transiently stale) flows store, confirm with GET /flows/<id> before giving up; only redirect to /all when the server doesn't know the flow either. A genuinely nonexistent id still redirects exactly as before.

Deliberately not included: the related welcome-overlay placeholder-cleanup defect (blank-check reads data from header_flows payloads, which is always null) noted in the E2E team's evidence log — it is tracked separately.

Test plan

  • New Jest suite use-get-refresh-flows-query.test.ts (5 tests): signal forwarded to both GETs; aborted fetch never writes the store and never toasts (first and second call); 500 still toasts; 403 stays silent.
  • All related suites pass: 16 suites / 124 tests across controllers/API/queries/flows, pages/FlowPage, hooks/flows, flowBuilderWelcome, createNewFlow.
  • tsc --noEmit: no new errors in touched files; Biome clean.
  • Deterministic reproduction (Playwright vs langflowai/langflow-nightly:latest, 1.12.0.dev7, LANGFLOW_AUTO_LOGIN=true, LANGFLOW_WORKERS=1): hold the back-nav header_flows response (server snapshot taken pre-create) until after POST+navigate while the single-flow GET is in flight.
    • Before: URL trail /flows → /flow/<id> → /all, no overlay/modal — the reported bug, reproduced on demand.
    • After (patched build mounted into the same image): stays on /flow/<id> with the welcome overlay rendered, same hostile timing.
  • Nonexistent flow id (/flow/00000000-…) still redirects to /all.
  • 9/9 back-nav + immediate-click iterations pass on the patched build with natural timing.

Refs oriontech-me/langflow-e2e#966

Summary by CodeRabbit

  • Bug Fixes

    • Flow loading now retrieves missing flows from the server before redirecting, reducing unnecessary “flow not found” redirects.
    • Cancelled flow requests no longer trigger error notifications or update stored flow data.
    • Other loading errors continue to display appropriate notifications, except expected 403 responses.
  • Tests

    • Added coverage for request cancellation, error handling, successful flow loading, and signal forwarding.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b2a83d59-bffe-4cde-9e7c-f13091dc84af

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Refresh-flow queries now forward abort signals and suppress cancellation notifications. FlowPage fetches missing route flows and applies them to the canvas before redirecting when retrieval or application fails.

Changes

Refresh flow cancellation

Layer / File(s) Summary
Propagate cancellation through refresh-flow requests
src/frontend/src/controllers/API/queries/flows/use-get-refresh-flows-query.ts
React Query’s AbortSignal is passed to both API requests, and cancellation errors no longer dispatch error state.
Validate refresh-flow outcomes
src/frontend/src/controllers/API/queries/flows/__tests__/use-get-refresh-flows-query.test.ts
Tests cover successful updates, cancellation during either request, server errors, and silent 403 handling.

Missing flow loading

Layer / File(s) Summary
Fetch missing route flows
src/frontend/src/pages/FlowPage/index.tsx
Missing route flows are fetched and applied to the canvas; failures redirect to /all.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReactQuery
  participant getFlowsFn
  participant ApiClient
  participant FlowsStore
  ReactQuery->>getFlowsFn: invoke with AbortSignal
  getFlowsFn->>ApiClient: request flows with signal
  getFlowsFn->>ApiClient: request components with signal
  ApiClient-->>getFlowsFn: flows or cancellation error
  getFlowsFn->>FlowsStore: setFlows on success
Loading

Suggested reviewers: cristhianzl

🚥 Pre-merge checks | ✅ 8 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Test Quality And Coverage ⚠️ Warning The new hook has 5 substantive async tests, but the new FlowPage fetch/redirect path has no direct test coverage; the only FlowPage test file is hotkeys. Add focused FlowPage tests for the missing-flow server fallback and stale/unmount cancellation path, then rerun the frontend suite.
✅ Passed checks (8 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Test Coverage For New Implementations ✅ Passed Added a real frontend regression suite use-get-refresh-flows-query.test.ts with 5 cases covering signal forwarding, cancellation, and error paths; naming matches convention.
Test File Naming And Structure ✅ Passed PASS: the new frontend test is correctly named in tests, uses describe/it with beforeEach, and covers success plus cancel/error/403 cases.
Excessive Mock Usage Warning ✅ Passed Mocks cover external stores/API/query wiring only; the test still exercises the hook's real cancellation and error-handling logic, matching nearby unit-test patterns.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main frontend race-condition fix around stale flows-list refetches bouncing a new flow back to the list.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/new-flow-stale-store-bounce

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.

@github-actions github-actions Bot added the bug Something isn't working label Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Test Coverage Advisor

No source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉

Advisory check only — never blocks merge.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 27, 2026
@erichare
erichare requested review from Cristhianzl and keval718 July 27, 2026 22:12

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/frontend/src/pages/FlowPage/index.tsx`:
- Around line 185-190: Update the async effect containing getFlow and
applyFlowToCanvas with an effect-local cancellation guard that is set during
cleanup when id changes or the component unmounts. Check the guard before
applying the fetched flow and before calling navigate("/all"), preserving the
existing behavior for the current active request.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 815314d3-c057-4614-b7c3-0bf7b81546c7

📥 Commits

Reviewing files that changed from the base of the PR and between d021b3e and 579209a.

📒 Files selected for processing (3)
  • src/frontend/src/controllers/API/queries/flows/__tests__/use-get-refresh-flows-query.test.ts
  • src/frontend/src/controllers/API/queries/flows/use-get-refresh-flows-query.ts
  • src/frontend/src/pages/FlowPage/index.tsx

Comment on lines +185 to +190
try {
const flow = await getFlow({ id: id! });
applyFlowToCanvas(flow);
} catch {
navigate("/all");
}

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard the async route check against stale effects.

Lines 185–190 can resolve after id changes or the component unmounts. The old request may then apply the previous route’s flow or call navigate("/all") after a newer route has loaded. Add an effect-local cancellation guard (or abort the request) before applying the flow and before redirecting.

Proposed fix
 useEffect(() => {
+  let cancelled = false;
+
   const awaitgetTypes = async () => {
     ...
         try {
           const flow = await getFlow({ id: id! });
+          if (cancelled) return;
           applyFlowToCanvas(flow);
         } catch {
+          if (cancelled) return;
           navigate("/all");
         }
         return;
     ...
   };
   awaitgetTypes();
+
+  return () => {
+    cancelled = true;
+  };
 }, [id, flows, currentFlowId, types]);
📝 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
try {
const flow = await getFlow({ id: id! });
applyFlowToCanvas(flow);
} catch {
navigate("/all");
}
useEffect(() => {
let cancelled = false;
const awaitgetTypes = async () => {
...
try {
const flow = await getFlow({ id: id! });
if (cancelled) return;
applyFlowToCanvas(flow);
} catch {
if (cancelled) return;
navigate("/all");
}
return;
...
};
awaitgetTypes();
return () => {
cancelled = true;
};
}, [id, flows, currentFlowId, types]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/src/pages/FlowPage/index.tsx` around lines 185 - 190, Update the
async effect containing getFlow and applyFlowToCanvas with an effect-local
cancellation guard that is set during cleanup when id changes or the component
unmounts. Check the guard before applying the fetched flow and before calling
navigate("/all"), preserving the existing behavior for the current active
request.

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 47%
47.26% (67394/142583) 70.22% (9477/13496) 45.72% (1551/3392)

Unit Test Results

Tests Skipped Failures Errors Time
5377 0 💤 0 ❌ 0 🔥 17m 32s ⏱️

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.13043% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.85%. Comparing base (077ec94) to head (e116b00).
⚠️ Report is 2 commits behind head on release-1.11.2.

Files with missing lines Patch % Lines
src/frontend/src/pages/FlowPage/index.tsx 18.18% 9 Missing ⚠️
...rc/pages/FlowPage/hooks/use-load-flow-for-route.ts 98.48% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##           release-1.11.2   #14288      +/-   ##
==================================================
- Coverage           61.23%   56.85%   -4.39%     
==================================================
  Files                2450     2318     -132     
  Lines              239484   235326    -4158     
  Branches            36256    34840    -1416     
==================================================
- Hits               146641   133785   -12856     
- Misses              91071    99769    +8698     
  Partials             1772     1772              
Flag Coverage Δ
frontend 52.99% <89.13%> (-6.86%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...s/API/queries/flows/use-get-refresh-flows-query.ts 97.19% <100.00%> (+51.36%) ⬆️
...rc/pages/FlowPage/hooks/use-load-flow-for-route.ts 98.48% <98.48%> (ø)
src/frontend/src/pages/FlowPage/index.tsx 27.42% <18.18%> (-21.17%) ⬇️

... and 631 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 28, 2026
…ack to the list

Clicking New Flow right after navigating back to the flows list could
silently do nothing: the flow was created and the app navigated to
/flow/<id>, but an in-flight header flows refetch - snapshotted BEFORE the
create - landed afterwards and rewrote the flows store without the new
flow, so FlowPage's existence guard bounced straight back to /all with no
overlay, no toast and no console error (and an orphan flow left behind).

Two layers:
- use-get-refresh-flows-query now forwards the query's AbortSignal into
  both HTTP calls, so a superseded fetch (create fires refetchQueries) or
  an unmounted observer aborts for real and its setFlows side effect never
  runs; cancellations no longer surface the could-not-load-flows toast.
- FlowPage's guard no longer treats the flows store as authoritative when
  the route id is missing: it confirms with GET /flows/<id> and only
  redirects to /all when the server does not know the flow either.

Repro (deterministic, Playwright against langflow-nightly): hold the
back-nav header_flows response until after the POST + navigate while the
single-flow GET is in flight -> before: /flows -> /flow/<id> -> /all
bounce, no welcome overlay; after: lands on the canvas with the welcome
overlay. Found by the E2E regression suite (oriontech-me/langflow-e2e#966,
filed as LE-2019); affects 1.10.1+ via #12575.
@erichare
erichare force-pushed the fix/new-flow-stale-store-bounce branch from 662b84d to be8180a Compare July 30, 2026 14:54
@erichare
erichare changed the base branch from release-1.12.0 to release-1.11.2 July 30, 2026 14:54
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 30, 2026
@viktoravelino

Copy link
Copy Markdown
Collaborator

Review notes — non-blocking

Verdict: LGTM. Root cause is real, fix is minimal and layered. A few small items:

🐛 Suggestions

  1. Silent catch in the FlowPage fallbacksrc/frontend/src/pages/FlowPage/index.tsx:188
    The bare catch swallows everything: a transient 500 or network blip on the confirm GET silently redirects to /all. Same outcome as before the PR, so not a regression, but a console.error in the catch would help debug the next incident like this one.

  2. Duplicationsrc/frontend/src/pages/FlowPage/index.tsx:185-190
    The fallback body (getFlow + applyFlowToCanvas) is exactly getFlowToAddToCanvas(id) defined below. Could be:

    try {
      await getFlowToAddToCanvas(id!);
    } catch {
      navigate("/all");
    }
  3. id! assertion — if id were ever undefined, this now issues GET /flows/undefined before redirecting (previously an immediate redirect). Route always provides it in practice — an early if (!id) bail would make the assertion honest.

🧪 Test gap

  • No unit test for the new FlowPage fallback path (store-miss → server-confirm → apply vs. redirect). The Playwright repro covers it end-to-end, but a Jest test on the effect would keep the guard from regressing quietly.

📝 Notes

  • Behavior change worth knowing: with the signal consumed, React Query now also cancels the fetch when the last observer unmounts — a quick mount/unmount no longer populates the store as a side effect. That's the intended semantics (the side-effect-after-unmount is the bug), and staleTime: 0 refetches on next mount, so it's fine — just broader than "superseded by refetchQueries".
  • Pre-existing effect race (not introduced here): the effect isn't cancelled on unmount, so a slow confirm GET can applyFlowToCanvas after the user navigated away. getFlowToAddToCanvas has the same shape — noting it since this PR is about exactly this class of bug.

@viktoravelino viktoravelino left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lgtm

@github-actions github-actions Bot added the lgtm This PR has been approved by a maintainer label Jul 30, 2026
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 30, 2026

Copy link
Copy Markdown
Member Author

Thanks @viktoravelino — addressed these in e116b006f5:

  • extracted the route-loading effect into a focused hook, with an early missing-id redirect and no id!
  • reused one load/apply path and now logs failed server confirmations before redirecting
  • added effect cleanup guards so stale success/failure results cannot apply or redirect after an ID change/unmount
  • added 5 Jest cases covering confirm/apply, failure/log/redirect, missing IDs, and both stale-result paths

Validation: Biome clean; 75 related suites / 1,005 tests passed, then the 10 focused tests were rerun after the concurrent release-branch merge.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 30, 2026
@erichare
erichare merged commit 6a8de81 into release-1.11.2 Jul 30, 2026
44 of 95 checks passed
@erichare
erichare deleted the fix/new-flow-stale-store-bounce branch July 30, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants