From ebf003b9f2942b8f0dbbba43f03655ff841792c1 Mon Sep 17 00:00:00 2001 From: Max Techera <[REDACTED_EMAIL]> Date: Wed, 4 Mar 2026 15:19:16 +0000 Subject: [PATCH 1/2] fix: add defensive checks to all debug logging Wraps all property access in optional chaining and try-catch blocks to prevent TypeError when objects are undefined. The 'filePath' error was likely from trying to log an object property that didn't exist, causing the logging itself to throw errors. Changes: - Use optional chaining (?.) for all object property access in logs - Wrap Object.keys() calls in try-catch - Add fallback values ('NONE', 'UNKNOWN', 'N/A') for undefined properties - Wrap entire logging blocks in try-catch where complex objects are logged This ensures debug logging never crashes the application. --- .../tools/MCP/Confluence/ConfluenceMCP.ts | 8 +++++-- .../nodes/tools/MCP/Jira/JiraMCP.ts | 8 +++++-- packages/components/src/utils.ts | 4 ++-- packages/server/src/utils/index.ts | 22 +++++++++++++------ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/packages/components/nodes/tools/MCP/Confluence/ConfluenceMCP.ts b/packages/components/nodes/tools/MCP/Confluence/ConfluenceMCP.ts index 0ae4e97710a..53cc40a1a15 100644 --- a/packages/components/nodes/tools/MCP/Confluence/ConfluenceMCP.ts +++ b/packages/components/nodes/tools/MCP/Confluence/ConfluenceMCP.ts @@ -103,8 +103,12 @@ class Confluence_MCP implements INode { async getTools(nodeData: INodeData, options: ICommonObject): Promise { console.log('[CONFLUENCE MCP DEBUG] ========== getTools START ==========') console.log('[CONFLUENCE MCP DEBUG] Timestamp:', new Date().toISOString()) - console.log('[CONFLUENCE MCP DEBUG] nodeData.credential ID:', nodeData.credential) - console.log('[CONFLUENCE MCP DEBUG] options keys:', Object.keys(options || {}).join(', ')) + console.log('[CONFLUENCE MCP DEBUG] nodeData.credential ID:', nodeData?.credential || 'NONE') + try { + console.log('[CONFLUENCE MCP DEBUG] options keys:', Object.keys(options || {}).join(', ')) + } catch (e) { + console.log('[CONFLUENCE MCP DEBUG] options keys: ERROR -', String(e)) + } console.time('[CONFLUENCE MCP DEBUG] getCredentialData') const credentialData = await getCredentialData(nodeData.credential ?? '', options) diff --git a/packages/components/nodes/tools/MCP/Jira/JiraMCP.ts b/packages/components/nodes/tools/MCP/Jira/JiraMCP.ts index 683a0509a8c..bffc69be37a 100644 --- a/packages/components/nodes/tools/MCP/Jira/JiraMCP.ts +++ b/packages/components/nodes/tools/MCP/Jira/JiraMCP.ts @@ -103,8 +103,12 @@ class Jira_MCP implements INode { async getTools(nodeData: INodeData, options: ICommonObject): Promise { console.log('[JIRA MCP DEBUG] ========== getTools START ==========') console.log('[JIRA MCP DEBUG] Timestamp:', new Date().toISOString()) - console.log('[JIRA MCP DEBUG] nodeData.credential ID:', nodeData.credential) - console.log('[JIRA MCP DEBUG] options keys:', Object.keys(options || {}).join(', ')) + console.log('[JIRA MCP DEBUG] nodeData.credential ID:', nodeData?.credential || 'NONE') + try { + console.log('[JIRA MCP DEBUG] options keys:', Object.keys(options || {}).join(', ')) + } catch (e) { + console.log('[JIRA MCP DEBUG] options keys: ERROR -', String(e)) + } console.time('[JIRA MCP DEBUG] getCredentialData') const credentialData = await getCredentialData(nodeData.credential ?? '', options) diff --git a/packages/components/src/utils.ts b/packages/components/src/utils.ts index 4b403f844b7..a62fcbee9bc 100644 --- a/packages/components/src/utils.ts +++ b/packages/components/src/utils.ts @@ -658,8 +658,8 @@ export const getCredentialData = async (selectedCredentialId: string, options: I return {} } - console.log('[getCredentialData DEBUG] Credential found, name:', credential.name) - console.log('[getCredentialData DEBUG] Credential workspaceId:', credential.workspaceId) + console.log('[getCredentialData DEBUG] Credential found, name:', credential?.name || 'N/A') + console.log('[getCredentialData DEBUG] Credential workspaceId:', credential?.workspaceId || 'N/A') // Decrypt credentialData console.time('[getCredentialData DEBUG] Decrypt credential') diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index 572f65ab306..b8248ceaa43 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -646,11 +646,15 @@ export const buildFlow = async ({ ) { initializedNodes.add(nodeId) } else { - console.log(`[NODE INIT DEBUG] ========== Starting node initialization ==========`) - console.log(`[NODE INIT DEBUG] Node: ${reactFlowNode.data.label} (${reactFlowNode.data.name})`) - console.log(`[NODE INIT DEBUG] Node ID: ${reactFlowNode.data.id}`) - console.log(`[NODE INIT DEBUG] Has credential: ${!!reactFlowNode.data.credential}`) - console.log(`[NODE INIT DEBUG] Timestamp: ${new Date().toISOString()}`) + try { + console.log(`[NODE INIT DEBUG] ========== Starting node initialization ==========`) + console.log(`[NODE INIT DEBUG] Node: ${reactFlowNode?.data?.label || 'UNKNOWN'} (${reactFlowNode?.data?.name || 'UNKNOWN'})`) + console.log(`[NODE INIT DEBUG] Node ID: ${reactFlowNode?.data?.id || 'UNKNOWN'}`) + console.log(`[NODE INIT DEBUG] Has credential: ${!!reactFlowNode?.data?.credential}`) + console.log(`[NODE INIT DEBUG] Timestamp: ${new Date().toISOString()}`) + } catch (e) { + console.log('[NODE INIT DEBUG] Error logging node info:', String(e)) + } // Check and refresh credentials before node initialization if needed console.time(`[NODE INIT DEBUG] checkAndRefreshCredentialsBeforeInit`) @@ -2122,8 +2126,12 @@ export const needsCredentialRefresh = async (credentialId: string, options: { ap * This ensures tokens are fresh before the node tries to use them */ export const checkAndRefreshCredentialsBeforeInit = async (reactFlowNode: IReactFlowNode, options: ICommonObject): Promise => { - console.log('[checkAndRefreshCredentialsBeforeInit DEBUG] Called for node:', reactFlowNode.data.name, reactFlowNode.data.id) - console.log('[checkAndRefreshCredentialsBeforeInit DEBUG] Timestamp:', new Date().toISOString()) + try { + console.log('[checkAndRefreshCredentialsBeforeInit DEBUG] Called for node:', reactFlowNode?.data?.name || 'UNKNOWN', reactFlowNode?.data?.id || 'UNKNOWN') + console.log('[checkAndRefreshCredentialsBeforeInit DEBUG] Timestamp:', new Date().toISOString()) + } catch (e) { + console.log('[checkAndRefreshCredentialsBeforeInit DEBUG] Error logging:', String(e)) + } try { // Check if this node requires OAuth refresh and has credentials From 442fb8e77a61af7991956c275a8a5d121ef31e4b Mon Sep 17 00:00:00 2001 From: Max Techera <[REDACTED_EMAIL]> Date: Wed, 4 Mar 2026 15:23:54 +0000 Subject: [PATCH 2/2] fix: add null checks before accessing componentNodes[].filePath Fixes TypeError: Cannot read properties of undefined (reading 'filePath') The buildFlow function was accessing componentNodes[nodeName].filePath without checking if the node exists in the component pool first. This causes crashes when: - A node isn't properly registered - A node name doesn't match the pool keys - The component pool hasn't finished loading Added guards in 3 locations: 1. buildFlow main loop (line 592) 2. clearSessionMemory (line 832) 3. getSessionChatHistory (line 1843) Now logs an error and continues instead of crashing the entire flow. --- packages/server/src/utils/index.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index b8248ceaa43..940c78d8b44 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -589,7 +589,12 @@ export const buildFlow = async ({ if (!reactFlowNode || reactFlowNode === undefined || nodeIndex < 0) continue try { - const nodeInstanceFilePath = componentNodes[reactFlowNode.data.name].filePath as string + const componentNode = componentNodes[reactFlowNode.data.name] + if (!componentNode) { + logger.error(`[buildFlow] Component node not found in pool: ${reactFlowNode.data.name} (id: ${reactFlowNode.id})`) + continue + } + const nodeInstanceFilePath = componentNode.filePath as string const nodeModule = await import(nodeInstanceFilePath) const newNodeInstance = new nodeModule.nodeClass() @@ -824,7 +829,12 @@ export const clearSessionMemory = async ( // Only clear specific session memory from View Message Dialog UI if (isClearFromViewMessageDialog && memoryType && node.data.label !== memoryType) continue - const nodeInstanceFilePath = componentNodes[node.data.name].filePath as string + const componentNode = componentNodes[node.data.name] + if (!componentNode) { + logger.warn(`[clearSessionMemory] Component node not found: ${node.data.name}`) + continue + } + const nodeInstanceFilePath = componentNode.filePath as string const nodeModule = await import(nodeInstanceFilePath) const newNodeInstance = new nodeModule.nodeClass() const options: ICommonObject = { @@ -1831,7 +1841,12 @@ export const getSessionChatHistory = async ( logger: any, prependMessages?: IMessage[] ): Promise => { - const nodeInstanceFilePath = componentNodes[memoryNode.data.name].filePath as string + const componentNode = componentNodes[memoryNode.data.name] + if (!componentNode) { + logger.error(`[getSessionChatHistory] Component node not found: ${memoryNode.data.name}`) + return [] + } + const nodeInstanceFilePath = componentNode.filePath as string const nodeModule = await import(nodeInstanceFilePath) const newNodeInstance = new nodeModule.nodeClass()