fix(flows): don't bounce off a working flow on a partial load error

The flow detail query runs with errorPolicy:'all', so a failed sibling field
(the flow's log lists are separate, nullable root query fields — terminalLogs,
messageLogs, etc.) surfaces an Apollo error while `flow` itself resolves fine.
Both the redirect effect and the provider's load-error toast keyed off "any
error present", so one flaky log resolver would kick the user back to /flows
and toast "Failed to load flow" over a flow that had actually loaded.

Gate both on the flow genuinely being absent (`!flowData?.flow`) instead of on
the presence of an error. A missing/invalid flow still redirects and toasts
(its non-null `flow` field propagates to a null result); a partial failure now
renders the flow and lets the affected panel show its own empty state.

Reachability confirmed from the schema (sibling log fields are `[X!]`, nullable;
`flow` is `Flow!`, non-null) — end-to-end confirmation folded into the P2-S5
live run. 996 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sergey Kozyrenko
2026-07-18 12:10:06 +07:00
co-authored by Claude Opus 4.8
parent 140578fd7c
commit c43c05c29b
2 changed files with 11 additions and 9 deletions
+6 -3
View File
@@ -79,7 +79,7 @@ function Flow() {
const { isDesktop, isMobile } = useBreakpoint();
const navigate = useNavigate();
const { flowData, flowError, flowId, isLoading: isFlowLoading } = useFlow();
const { flowData, flowId, isLoading: isFlowLoading } = useFlow();
const { deleteFlow, finishFlow } = useFlows();
const { isFavoriteFlow, toggleFavoriteFlow } = useFavorites();
@@ -108,10 +108,13 @@ function Flow() {
const [renameFlowMutation, { loading: isRenameLoading }] = useMutation(RenameFlowDocument);
useEffect(() => {
if (flowError || (!isFlowLoading && !flowData?.flow)) {
// errorPolicy:'all' surfaces a partial error while the flow itself
// loaded; leave only when the flow is genuinely absent, not on any error
// — else a failed sibling log query bounces the user off a working flow.
if (!isFlowLoading && !flowData?.flow) {
navigate(routes.flows, { replace: true });
}
}, [flowError, flowData, isFlowLoading, navigate]);
}, [flowData, isFlowLoading, navigate]);
const handleFlowRenameSave = useCallback(async () => {
const newTitle = editingInputRef.current?.value.trim();
+5 -6
View File
@@ -170,12 +170,11 @@ export function FlowProvider({ children }: FlowProviderProps) {
const flowStatus = useMemo(() => flowData?.flow?.status, [flowData?.flow?.status]);
// A single Postgres "no rows in result set" surfaces here every time a sibling
// query/subscription retries against an invalid flow id; without a stable
// toast id Sonner would stack 8 copies of the same message before the page
// redirects. Surface a friendly message and drop the raw SQL detail entirely.
// errorPolicy:'all' surfaces a partial error while the flow loaded, so gate
// on `!flow` or a partial failure toasts over a flow that rendered fine. The
// stable id keeps the invalid-id "no rows" retries from stacking.
useEffect(() => {
if (flowError) {
if (flowError && !flowData?.flow) {
const raw = flowError.message ?? '';
const isNotFound = /no rows in result set|not found/i.test(raw);
toast.error(isNotFound ? 'Flow not found' : 'Failed to load flow', {
@@ -184,7 +183,7 @@ export function FlowProvider({ children }: FlowProviderProps) {
});
Log.error('Error loading flow:', flowError);
}
}, [flowError]);
}, [flowError, flowData]);
const submitAutomationMessage = useCallback(
async (values: FlowFormValues) => {