From 8bf2e69942d4e6cedc1c88464ba5a7813baede5e Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Sat, 24 Jan 2026 01:37:17 -0800 Subject: [PATCH 1/6] fix(child-workflow): nested spans handoff --- .../executor/errors/child-workflow-error.ts | 31 ++++++++++++++++ apps/sim/executor/execution/block-executor.ts | 23 +++++++----- .../handlers/workflow/workflow-handler.ts | 36 +++++++++---------- 3 files changed, 63 insertions(+), 27 deletions(-) create mode 100644 apps/sim/executor/errors/child-workflow-error.ts diff --git a/apps/sim/executor/errors/child-workflow-error.ts b/apps/sim/executor/errors/child-workflow-error.ts new file mode 100644 index 0000000000..373b42444a --- /dev/null +++ b/apps/sim/executor/errors/child-workflow-error.ts @@ -0,0 +1,31 @@ +import type { TraceSpan } from '@/lib/logs/types' +import type { ExecutionResult } from '@/executor/types' + +interface ChildWorkflowErrorOptions { + message: string + childWorkflowName: string + childTraceSpans?: TraceSpan[] + executionResult?: ExecutionResult + cause?: Error +} + +/** + * Error raised when a child workflow execution fails. + */ +export class ChildWorkflowError extends Error { + readonly childTraceSpans: TraceSpan[] + readonly childWorkflowName: string + readonly executionResult?: ExecutionResult + + constructor(options: ChildWorkflowErrorOptions) { + super(options.message, { cause: options.cause }) + this.name = 'ChildWorkflowError' + this.childWorkflowName = options.childWorkflowName + this.childTraceSpans = options.childTraceSpans ?? [] + this.executionResult = options.executionResult + } + + static isChildWorkflowError(error: unknown): error is ChildWorkflowError { + return error instanceof ChildWorkflowError + } +} diff --git a/apps/sim/executor/execution/block-executor.ts b/apps/sim/executor/execution/block-executor.ts index 5a271f7f18..59ae418f74 100644 --- a/apps/sim/executor/execution/block-executor.ts +++ b/apps/sim/executor/execution/block-executor.ts @@ -13,6 +13,7 @@ import { isSentinelBlockType, } from '@/executor/constants' import type { DAGNode } from '@/executor/dag/builder' +import { ChildWorkflowError } from '@/executor/errors/child-workflow-error' import type { BlockStateWriter, ContextExtensions } from '@/executor/execution/types' import { generatePauseContextId, @@ -213,24 +214,28 @@ export class BlockExecutor { ? resolvedInputs : ((block.config?.params as Record | undefined) ?? {}) - if (blockLog) { - blockLog.endedAt = new Date().toISOString() - blockLog.durationMs = duration - blockLog.success = false - blockLog.error = errorMessage - blockLog.input = input - } - const errorOutput: NormalizedBlockOutput = { error: errorMessage, } - if (error && typeof error === 'object' && 'childTraceSpans' in error) { + if (ChildWorkflowError.isChildWorkflowError(error)) { + errorOutput.childTraceSpans = error.childTraceSpans + errorOutput.childWorkflowName = error.childWorkflowName + } else if (error && typeof error === 'object' && 'childTraceSpans' in error) { errorOutput.childTraceSpans = (error as any).childTraceSpans } this.state.setBlockOutput(node.id, errorOutput, duration) + if (blockLog) { + blockLog.endedAt = new Date().toISOString() + blockLog.durationMs = duration + blockLog.success = false + blockLog.error = errorMessage + blockLog.input = input + blockLog.output = this.filterOutputForLog(block, errorOutput) + } + logger.error( phase === 'input_resolution' ? 'Failed to resolve block inputs' : 'Block execution failed', { diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index d8f7ced358..7ee6dec6ec 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -4,6 +4,7 @@ import type { TraceSpan } from '@/lib/logs/types' import type { BlockOutput } from '@/blocks/types' import { Executor } from '@/executor' import { BlockType, DEFAULTS, HTTP } from '@/executor/constants' +import { ChildWorkflowError } from '@/executor/errors/child-workflow-error' import type { BlockHandler, ExecutionContext, @@ -145,31 +146,30 @@ export class WorkflowBlockHandler implements BlockHandler { const childWorkflowName = workflowMetadata?.name || workflowId const originalError = error.message || 'Unknown error' - const wrappedError = new Error( - `Error in child workflow "${childWorkflowName}": ${originalError}` - ) - + let childTraceSpans: WorkflowTraceSpan[] = [] + let executionResult: ExecutionResult | undefined if (error.executionResult?.logs) { - const executionResult = error.executionResult as ExecutionResult + executionResult = error.executionResult as ExecutionResult logger.info(`Extracting child trace spans from error.executionResult`, { hasLogs: (executionResult.logs?.length ?? 0) > 0, logCount: executionResult.logs?.length ?? 0, }) - const childTraceSpans = this.captureChildWorkflowLogs( - executionResult, - childWorkflowName, - ctx - ) + childTraceSpans = this.captureChildWorkflowLogs(executionResult, childWorkflowName, ctx) logger.info(`Captured ${childTraceSpans.length} child trace spans from failed execution`) - ;(wrappedError as any).childTraceSpans = childTraceSpans } else if (error.childTraceSpans && Array.isArray(error.childTraceSpans)) { - ;(wrappedError as any).childTraceSpans = error.childTraceSpans + childTraceSpans = error.childTraceSpans } - throw wrappedError + throw new ChildWorkflowError({ + message: `Error in child workflow "${childWorkflowName}": ${originalError}`, + childWorkflowName, + childTraceSpans, + executionResult, + cause: error, + }) } } @@ -441,11 +441,11 @@ export class WorkflowBlockHandler implements BlockHandler { if (!success) { logger.warn(`Child workflow ${childWorkflowName} failed`) - const error = new Error( - `Error in child workflow "${childWorkflowName}": ${childResult.error || 'Child workflow execution failed'}` - ) - ;(error as any).childTraceSpans = childTraceSpans || [] - throw error + throw new ChildWorkflowError({ + message: `Error in child workflow "${childWorkflowName}": ${childResult.error || 'Child workflow execution failed'}`, + childWorkflowName, + childTraceSpans: childTraceSpans || [], + }) } return { From 587d44ad6f8898817031e4d19df683ea210ee765 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Sat, 24 Jan 2026 01:44:53 -0800 Subject: [PATCH 2/6] remove overly defensive programming --- apps/sim/executor/execution/block-executor.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/sim/executor/execution/block-executor.ts b/apps/sim/executor/execution/block-executor.ts index 59ae418f74..5e2ec09cc5 100644 --- a/apps/sim/executor/execution/block-executor.ts +++ b/apps/sim/executor/execution/block-executor.ts @@ -221,8 +221,6 @@ export class BlockExecutor { if (ChildWorkflowError.isChildWorkflowError(error)) { errorOutput.childTraceSpans = error.childTraceSpans errorOutput.childWorkflowName = error.childWorkflowName - } else if (error && typeof error === 'object' && 'childTraceSpans' in error) { - errorOutput.childTraceSpans = (error as any).childTraceSpans } this.state.setBlockOutput(node.id, errorOutput, duration) From d3f20311d047c0cc42332cb695cdfa653570a616 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Sat, 24 Jan 2026 01:45:03 -0800 Subject: [PATCH 3/6] update type check --- apps/sim/executor/handlers/workflow/workflow-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 7ee6dec6ec..7bd2955024 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -159,7 +159,7 @@ export class WorkflowBlockHandler implements BlockHandler { childTraceSpans = this.captureChildWorkflowLogs(executionResult, childWorkflowName, ctx) logger.info(`Captured ${childTraceSpans.length} child trace spans from failed execution`) - } else if (error.childTraceSpans && Array.isArray(error.childTraceSpans)) { + } else if (ChildWorkflowError.isChildWorkflowError(error)) { childTraceSpans = error.childTraceSpans } From 594bcac5f28b920e794a93e8f0e1626975414fa0 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Sat, 24 Jan 2026 01:54:09 -0800 Subject: [PATCH 4/6] type more code --- .../app/api/workflows/[id]/execute/route.ts | 15 +++++----- .../hooks/use-workflow-execution.ts | 16 ++-------- apps/sim/executor/execution/engine.ts | 6 ++-- .../handlers/workflow/workflow-handler.ts | 6 ++-- apps/sim/executor/utils/errors.ts | 29 ++++++++++++++++++- apps/sim/hooks/use-execution-stream.ts | 9 ++++-- 6 files changed, 53 insertions(+), 28 deletions(-) diff --git a/apps/sim/app/api/workflows/[id]/execute/route.ts b/apps/sim/app/api/workflows/[id]/execute/route.ts index 217d26f9bc..856a1a3c94 100644 --- a/apps/sim/app/api/workflows/[id]/execute/route.ts +++ b/apps/sim/app/api/workflows/[id]/execute/route.ts @@ -30,6 +30,7 @@ import { normalizeName } from '@/executor/constants' import { ExecutionSnapshot } from '@/executor/execution/snapshot' import type { ExecutionMetadata, IterationContext } from '@/executor/execution/types' import type { NormalizedBlockOutput, StreamingExecution } from '@/executor/types' +import { hasExecutionResult } from '@/executor/utils/errors' import { Serializer } from '@/serializer' import { CORE_TRIGGER_TYPES, type CoreTriggerType } from '@/stores/logs/filters/types' @@ -467,17 +468,17 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: } return NextResponse.json(filteredResult) - } catch (error: any) { - const errorMessage = error.message || 'Unknown error' + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error' logger.error(`[${requestId}] Non-SSE execution failed: ${errorMessage}`) - const executionResult = error.executionResult + const executionResult = hasExecutionResult(error) ? error.executionResult : undefined return NextResponse.json( { success: false, output: executionResult?.output, - error: executionResult?.error || error.message || 'Execution failed', + error: executionResult?.error || errorMessage || 'Execution failed', metadata: executionResult?.metadata ? { duration: executionResult.metadata.duration, @@ -788,11 +789,11 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: // Cleanup base64 cache for this execution await cleanupExecutionBase64Cache(executionId) - } catch (error: any) { - const errorMessage = error.message || 'Unknown error' + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error' logger.error(`[${requestId}] SSE execution failed: ${errorMessage}`) - const executionResult = error.executionResult + const executionResult = hasExecutionResult(error) ? error.executionResult : undefined sendEvent({ type: 'execution:error', diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts index fbd432c12e..1c0cbcc7a0 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts @@ -16,6 +16,7 @@ import { } from '@/lib/workflows/triggers/triggers' import { useCurrentWorkflow } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-current-workflow' import type { BlockLog, ExecutionResult, StreamingExecution } from '@/executor/types' +import { hasExecutionResult } from '@/executor/utils/errors' import { coerceValue } from '@/executor/utils/start-block' import { subscriptionKeys } from '@/hooks/queries/subscription' import { useExecutionStream } from '@/hooks/use-execution-stream' @@ -76,17 +77,6 @@ function normalizeErrorMessage(error: unknown): string { return WORKFLOW_EXECUTION_FAILURE_MESSAGE } -function isExecutionResult(value: unknown): value is ExecutionResult { - if (!isRecord(value)) return false - return typeof value.success === 'boolean' && isRecord(value.output) -} - -function extractExecutionResult(error: unknown): ExecutionResult | null { - if (!isRecord(error)) return null - const candidate = error.executionResult - return isExecutionResult(candidate) ? candidate : null -} - export function useWorkflowExecution() { const queryClient = useQueryClient() const currentWorkflow = useCurrentWorkflow() @@ -1138,11 +1128,11 @@ export function useWorkflowExecution() { const handleExecutionError = (error: unknown, options?: { executionId?: string }) => { const normalizedMessage = normalizeErrorMessage(error) - const executionResultFromError = extractExecutionResult(error) let errorResult: ExecutionResult - if (executionResultFromError) { + if (hasExecutionResult(error)) { + const executionResultFromError = error.executionResult const logs = Array.isArray(executionResultFromError.logs) ? executionResultFromError.logs : [] errorResult = { diff --git a/apps/sim/executor/execution/engine.ts b/apps/sim/executor/execution/engine.ts index 58792ef2b8..05e7e04843 100644 --- a/apps/sim/executor/execution/engine.ts +++ b/apps/sim/executor/execution/engine.ts @@ -13,7 +13,7 @@ import type { PausePoint, ResumeStatus, } from '@/executor/types' -import { normalizeError } from '@/executor/utils/errors' +import { attachExecutionResult, normalizeError } from '@/executor/utils/errors' const logger = createLogger('ExecutionEngine') @@ -170,8 +170,8 @@ export class ExecutionEngine { metadata: this.context.metadata, } - if (error && typeof error === 'object') { - ;(error as any).executionResult = executionResult + if (error instanceof Error) { + attachExecutionResult(error, executionResult) } throw error } diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 7bd2955024..6759af5ff5 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -11,6 +11,7 @@ import type { ExecutionResult, StreamingExecution, } from '@/executor/types' +import { hasExecutionResult } from '@/executor/utils/errors' import { buildAPIUrl, buildAuthHeaders } from '@/executor/utils/http' import { parseJSON } from '@/executor/utils/json' import { lazyCleanupInputMapping } from '@/executor/utils/lazy-cleanup' @@ -148,8 +149,9 @@ export class WorkflowBlockHandler implements BlockHandler { const originalError = error.message || 'Unknown error' let childTraceSpans: WorkflowTraceSpan[] = [] let executionResult: ExecutionResult | undefined - if (error.executionResult?.logs) { - executionResult = error.executionResult as ExecutionResult + + if (hasExecutionResult(error) && error.executionResult.logs) { + executionResult = error.executionResult logger.info(`Extracting child trace spans from error.executionResult`, { hasLogs: (executionResult.logs?.length ?? 0) > 0, diff --git a/apps/sim/executor/utils/errors.ts b/apps/sim/executor/utils/errors.ts index 0f078d84e1..3144d9c715 100644 --- a/apps/sim/executor/utils/errors.ts +++ b/apps/sim/executor/utils/errors.ts @@ -1,6 +1,33 @@ -import type { ExecutionContext } from '@/executor/types' +import type { ExecutionContext, ExecutionResult } from '@/executor/types' import type { SerializedBlock } from '@/serializer/types' +/** + * Interface for errors that carry an ExecutionResult. + * Used when workflow execution fails and we want to preserve partial results. + */ +export interface ErrorWithExecutionResult extends Error { + executionResult: ExecutionResult +} + +/** + * Type guard to check if an error carries an ExecutionResult. + */ +export function hasExecutionResult(error: unknown): error is ErrorWithExecutionResult { + return ( + error instanceof Error && + 'executionResult' in error && + error.executionResult != null && + typeof error.executionResult === 'object' + ) +} + +/** + * Attaches an ExecutionResult to an error for propagation to parent workflows. + */ +export function attachExecutionResult(error: Error, executionResult: ExecutionResult): void { + Object.assign(error, { executionResult }) +} + export interface BlockExecutionErrorDetails { block: SerializedBlock error: Error | string diff --git a/apps/sim/hooks/use-execution-stream.ts b/apps/sim/hooks/use-execution-stream.ts index 3516c93664..b9d1cc6858 100644 --- a/apps/sim/hooks/use-execution-stream.ts +++ b/apps/sim/hooks/use-execution-stream.ts @@ -100,8 +100,13 @@ export function useExecutionStream() { }) if (!response.ok) { - const error = await response.json() - throw new Error(error.error || 'Failed to start execution') + const errorResponse = await response.json() + const error = new Error(errorResponse.error || 'Failed to start execution') + // Attach the execution result from server response for error handling + if (errorResponse && typeof errorResponse === 'object') { + Object.assign(error, { executionResult: errorResponse }) + } + throw error } if (!response.body) { From eb767b5edea67336b003e594c9ee3edc504b595c Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Sat, 24 Jan 2026 01:58:02 -0800 Subject: [PATCH 5/6] remove more dead code --- apps/sim/background/schedule-execution.ts | 5 ++--- apps/sim/background/webhook-execution.ts | 15 ++++++++------- apps/sim/background/workflow-execution.ts | 5 ++--- .../handlers/workflow/workflow-handler.ts | 6 +++--- apps/sim/lib/workflows/executor/execution-core.ts | 12 ++++-------- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/apps/sim/background/schedule-execution.ts b/apps/sim/background/schedule-execution.ts index 54ce24e5ea..c52676b8df 100644 --- a/apps/sim/background/schedule-execution.ts +++ b/apps/sim/background/schedule-execution.ts @@ -21,7 +21,7 @@ import { } from '@/lib/workflows/schedules/utils' import { ExecutionSnapshot } from '@/executor/execution/snapshot' import type { ExecutionMetadata } from '@/executor/execution/types' -import type { ExecutionResult } from '@/executor/types' +import { hasExecutionResult } from '@/executor/utils/errors' import { MAX_CONSECUTIVE_FAILURES } from '@/triggers/constants' const logger = createLogger('TriggerScheduleExecution') @@ -231,8 +231,7 @@ async function runWorkflowExecution({ } catch (error: unknown) { logger.error(`[${requestId}] Early failure in scheduled workflow ${payload.workflowId}`, error) - const errorWithResult = error as { executionResult?: ExecutionResult } - const executionResult = errorWithResult?.executionResult + const executionResult = hasExecutionResult(error) ? error.executionResult : undefined const { traceSpans } = executionResult ? buildTraceSpans(executionResult) : { traceSpans: [] } await loggingSession.safeCompleteWithError({ diff --git a/apps/sim/background/webhook-execution.ts b/apps/sim/background/webhook-execution.ts index a32ce09021..e5e3d3007f 100644 --- a/apps/sim/background/webhook-execution.ts +++ b/apps/sim/background/webhook-execution.ts @@ -16,7 +16,7 @@ import { loadDeployedWorkflowState } from '@/lib/workflows/persistence/utils' import { getWorkflowById } from '@/lib/workflows/utils' import { ExecutionSnapshot } from '@/executor/execution/snapshot' import type { ExecutionMetadata } from '@/executor/execution/types' -import type { ExecutionResult } from '@/executor/types' +import { hasExecutionResult } from '@/executor/utils/errors' import { safeAssign } from '@/tools/safe-assign' import { getTrigger, isTriggerValid } from '@/triggers' @@ -578,12 +578,13 @@ async function executeWebhookJobInternal( deploymentVersionId, }) - const errorWithResult = error as { executionResult?: ExecutionResult } - const executionResult = errorWithResult?.executionResult || { - success: false, - output: {}, - logs: [], - } + const executionResult = hasExecutionResult(error) + ? error.executionResult + : { + success: false, + output: {}, + logs: [], + } const { traceSpans } = buildTraceSpans(executionResult) await loggingSession.safeCompleteWithError({ diff --git a/apps/sim/background/workflow-execution.ts b/apps/sim/background/workflow-execution.ts index f7e061fbf0..99e83d54cc 100644 --- a/apps/sim/background/workflow-execution.ts +++ b/apps/sim/background/workflow-execution.ts @@ -9,7 +9,7 @@ import { PauseResumeManager } from '@/lib/workflows/executor/human-in-the-loop-m import { getWorkflowById } from '@/lib/workflows/utils' import { ExecutionSnapshot } from '@/executor/execution/snapshot' import type { ExecutionMetadata } from '@/executor/execution/types' -import type { ExecutionResult } from '@/executor/types' +import { hasExecutionResult } from '@/executor/utils/errors' import type { CoreTriggerType } from '@/stores/logs/filters/types' const logger = createLogger('TriggerWorkflowExecution') @@ -160,8 +160,7 @@ export async function executeWorkflowJob(payload: WorkflowExecutionPayload) { executionId, }) - const errorWithResult = error as { executionResult?: ExecutionResult } - const executionResult = errorWithResult?.executionResult + const executionResult = hasExecutionResult(error) ? error.executionResult : undefined const { traceSpans } = executionResult ? buildTraceSpans(executionResult) : { traceSpans: [] } await loggingSession.safeCompleteWithError({ diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 6759af5ff5..3ec7319ebf 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -139,14 +139,14 @@ export class WorkflowBlockHandler implements BlockHandler { ) return mappedResult - } catch (error: any) { + } catch (error: unknown) { logger.error(`Error executing child workflow ${workflowId}:`, error) const { workflows } = useWorkflowRegistry.getState() const workflowMetadata = workflows[workflowId] const childWorkflowName = workflowMetadata?.name || workflowId - const originalError = error.message || 'Unknown error' + const originalError = error instanceof Error ? error.message : 'Unknown error' let childTraceSpans: WorkflowTraceSpan[] = [] let executionResult: ExecutionResult | undefined @@ -170,7 +170,7 @@ export class WorkflowBlockHandler implements BlockHandler { childWorkflowName, childTraceSpans, executionResult, - cause: error, + cause: error instanceof Error ? error : undefined, }) } } diff --git a/apps/sim/lib/workflows/executor/execution-core.ts b/apps/sim/lib/workflows/executor/execution-core.ts index d128d9e208..c2b300f084 100644 --- a/apps/sim/lib/workflows/executor/execution-core.ts +++ b/apps/sim/lib/workflows/executor/execution-core.ts @@ -24,6 +24,7 @@ import type { IterationContext, } from '@/executor/execution/types' import type { ExecutionResult, NormalizedBlockOutput } from '@/executor/types' +import { hasExecutionResult } from '@/executor/utils/errors' import { Serializer } from '@/serializer' import { mergeSubblockState } from '@/stores/workflows/server-utils' @@ -383,20 +384,15 @@ export async function executeWorkflowCore( } catch (error: unknown) { logger.error(`[${requestId}] Execution failed:`, error) - const errorWithResult = error as { - executionResult?: ExecutionResult - message?: string - stack?: string - } - const executionResult = errorWithResult?.executionResult + const executionResult = hasExecutionResult(error) ? error.executionResult : undefined const { traceSpans } = executionResult ? buildTraceSpans(executionResult) : { traceSpans: [] } await loggingSession.safeCompleteWithError({ endedAt: new Date().toISOString(), totalDurationMs: executionResult?.metadata?.duration || 0, error: { - message: errorWithResult?.message || 'Execution failed', - stackTrace: errorWithResult?.stack, + message: error instanceof Error ? error.message : 'Execution failed', + stackTrace: error instanceof Error ? error.stack : undefined, }, traceSpans, }) From bf22dd75ad7a44fb6293a5a214e3d06fd14e57be Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Sat, 24 Jan 2026 02:13:06 -0800 Subject: [PATCH 6/6] address bugbot comments --- apps/sim/executor/utils/errors.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/sim/executor/utils/errors.ts b/apps/sim/executor/utils/errors.ts index 3144d9c715..f92c9c1ff4 100644 --- a/apps/sim/executor/utils/errors.ts +++ b/apps/sim/executor/utils/errors.ts @@ -11,14 +11,20 @@ export interface ErrorWithExecutionResult extends Error { /** * Type guard to check if an error carries an ExecutionResult. + * Validates that executionResult has required fields (success, output). */ export function hasExecutionResult(error: unknown): error is ErrorWithExecutionResult { - return ( - error instanceof Error && - 'executionResult' in error && - error.executionResult != null && - typeof error.executionResult === 'object' - ) + if ( + !(error instanceof Error) || + !('executionResult' in error) || + error.executionResult == null || + typeof error.executionResult !== 'object' + ) { + return false + } + + const result = error.executionResult as Record + return typeof result.success === 'boolean' && result.output != null } /**