From 7e6e5b8a1dd87d9f6f37b1cc5cdbc0cfaf7a0d5e Mon Sep 17 00:00:00 2001 From: Siddharth Chauhan Date: Thu, 13 Nov 2025 22:29:38 +0530 Subject: [PATCH] fix: agentflow list refresh after deletion (Issue #5360) Root Cause: When deleting an Agentflow from the list view, updateFlowsApi.request() was called without pagination parameters. This caused the backend to return a plain array instead of the expected { data: [], total: 0 } format, leading to a TypeError when accessing .data.length. Solution: - Created refreshAgentflows callback that includes current pagination state - Passed callback through FlowListTable to FlowListMenu - Updated handleDelete to use callback instead of direct API call - Maintains backward compatibility for components without callback Fixes #5360 --- .../src/ui-component/button/FlowListMenu.jsx | 10 +++++-- .../src/ui-component/table/FlowListTable.jsx | 3 ++ packages/ui/src/views/agentflows/index.jsx | 29 ++++++++++++++----- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/packages/ui/src/ui-component/button/FlowListMenu.jsx b/packages/ui/src/ui-component/button/FlowListMenu.jsx index d3c2e259be5..0229666c316 100644 --- a/packages/ui/src/ui-component/button/FlowListMenu.jsx +++ b/packages/ui/src/ui-component/button/FlowListMenu.jsx @@ -74,7 +74,7 @@ const StyledMenu = styled((props) => ( } })) -export default function FlowListMenu({ chatflow, isAgentCanvas, isAgentflowV2, setError, updateFlowsApi }) { +export default function FlowListMenu({ chatflow, isAgentCanvas, isAgentflowV2, setError, updateFlowsApi, onRefresh }) { const { confirm } = useConfirm() const dispatch = useDispatch() const updateChatflowApi = useApi(chatflowsApi.updateChatflow) @@ -241,7 +241,10 @@ export default function FlowListMenu({ chatflow, isAgentCanvas, isAgentflowV2, s if (isConfirmed) { try { await chatflowsApi.deleteChatflow(chatflow.id) - if (isAgentCanvas && isAgentflowV2) { + // Use onRefresh callback if available (for agentflows with pagination), otherwise fallback to updateFlowsApi + if (onRefresh) { + onRefresh() + } else if (isAgentCanvas && isAgentflowV2) { await updateFlowsApi.request('AGENTFLOW') } else { await updateFlowsApi.request(isAgentCanvas ? 'MULTIAGENT' : undefined) @@ -454,5 +457,6 @@ FlowListMenu.propTypes = { isAgentCanvas: PropTypes.bool, isAgentflowV2: PropTypes.bool, setError: PropTypes.func, - updateFlowsApi: PropTypes.object + updateFlowsApi: PropTypes.object, + onRefresh: PropTypes.func } diff --git a/packages/ui/src/ui-component/table/FlowListTable.jsx b/packages/ui/src/ui-component/table/FlowListTable.jsx index a61df9ed2ed..29cb7cd0604 100644 --- a/packages/ui/src/ui-component/table/FlowListTable.jsx +++ b/packages/ui/src/ui-component/table/FlowListTable.jsx @@ -57,6 +57,7 @@ export const FlowListTable = ({ isLoading, filterFunction, updateFlowsApi, + onRefresh, setError, isAgentCanvas, isAgentflowV2 @@ -331,6 +332,7 @@ export const FlowListTable = ({ chatflow={row} setError={setError} updateFlowsApi={updateFlowsApi} + onRefresh={onRefresh} /> @@ -353,6 +355,7 @@ FlowListTable.propTypes = { isLoading: PropTypes.bool, filterFunction: PropTypes.func, updateFlowsApi: PropTypes.object, + onRefresh: PropTypes.func, setError: PropTypes.func, isAgentCanvas: PropTypes.bool, isAgentflowV2: PropTypes.bool diff --git a/packages/ui/src/views/agentflows/index.jsx b/packages/ui/src/views/agentflows/index.jsx index b82bfab8a76..ae485e6be74 100644 --- a/packages/ui/src/views/agentflows/index.jsx +++ b/packages/ui/src/views/agentflows/index.jsx @@ -68,6 +68,10 @@ const Agentflows = () => { getAllAgentflows.request(nextView === 'v2' ? 'AGENTFLOW' : 'MULTIAGENT', params) } + const refreshAgentflows = () => { + refresh(currentPage, pageLimit, agentflowVersion) + } + const handleChange = (event, nextView) => { if (nextView === null) return localStorage.setItem('flowDisplayStyle', nextView) @@ -138,21 +142,27 @@ const Agentflows = () => { setTotal(getAllAgentflows.data?.total) const images = {} const icons = {} - for (let i = 0; i < agentflows.length; i += 1) { - const flowDataStr = agentflows[i].flowData + + // Safety check: handle both array and object response formats + const flowsArray = Array.isArray(getAllAgentflows.data) + ? getAllAgentflows.data + : (agentflows || []) + + for (let i = 0; i < flowsArray.length; i += 1) { + const flowDataStr = flowsArray[i].flowData const flowData = JSON.parse(flowDataStr) const nodes = flowData.nodes || [] - images[agentflows[i].id] = [] - icons[agentflows[i].id] = [] + images[flowsArray[i].id] = [] + icons[flowsArray[i].id] = [] for (let j = 0; j < nodes.length; j += 1) { if (nodes[j].data.name === 'stickyNote' || nodes[j].data.name === 'stickyNoteAgentflow') continue const foundIcon = AGENTFLOW_ICONS.find((icon) => icon.name === nodes[j].data.name) if (foundIcon) { - icons[agentflows[i].id].push(foundIcon) + icons[flowsArray[i].id].push(foundIcon) } else { const imageSrc = `${baseURL}/api/v1/node-icon/${nodes[j].data.name}` - if (!images[agentflows[i].id].some((img) => img.imageSrc === imageSrc)) { - images[agentflows[i].id].push({ + if (!images[flowsArray[i].id].some((img) => img.imageSrc === imageSrc)) { + images[flowsArray[i].id].push({ imageSrc, label: nodes[j].data.label }) @@ -163,7 +173,9 @@ const Agentflows = () => { setImages(images) setIcons(icons) } catch (e) { - console.error(e) + console.error('❌ Error processing agentflows data:', e) + console.error('Data that caused error:', getAllAgentflows.data) + console.error('Error stack:', e.stack) } } }, [getAllAgentflows.data]) @@ -324,6 +336,7 @@ const Agentflows = () => { isLoading={isLoading} filterFunction={filterFlows} updateFlowsApi={getAllAgentflows} + onRefresh={refreshAgentflows} setError={setError} /> )}