= {}
- const workflowSpan = traceSpans[0]
- if (workflowSpan?.children && Array.isArray(workflowSpan.children)) {
- const traceSpansByBlockId = workflowSpan.children.reduce((acc: any, span: any) => {
+ logger.debug('Processing trace spans for frozen canvas:', { traceSpans })
+
+ // Recursively collect all spans with blockId from the trace spans tree
+ const collectBlockSpans = (spans: any[]): any[] => {
+ const blockSpans: any[] = []
+
+ for (const span of spans) {
+ // If this span has a blockId, it's a block execution
if (span.blockId) {
- if (!acc[span.blockId]) {
- acc[span.blockId] = []
- }
- acc[span.blockId].push(span)
+ blockSpans.push(span)
}
- return acc
- }, {})
+
+ // Recursively check children
+ if (span.children && Array.isArray(span.children)) {
+ blockSpans.push(...collectBlockSpans(span.children))
+ }
+ }
+
+ return blockSpans
+ }
+
+ const allBlockSpans = collectBlockSpans(traceSpans)
+ logger.debug('Collected all block spans:', allBlockSpans)
+
+ // Group spans by blockId
+ const traceSpansByBlockId = allBlockSpans.reduce((acc: any, span: any) => {
+ if (span.blockId) {
+ if (!acc[span.blockId]) {
+ acc[span.blockId] = []
+ }
+ acc[span.blockId].push(span)
+ }
+ return acc
+ }, {})
+
+ logger.debug('Grouped trace spans by blockId:', traceSpansByBlockId)
for (const [blockId, spans] of Object.entries(traceSpansByBlockId)) {
const spanArray = spans as any[]
@@ -407,10 +493,9 @@ export function FrozenCanvas({
totalIterations: iterations.length,
}
}
- }
- setBlockExecutions(blockExecutionMap)
- }
+ setBlockExecutions(blockExecutionMap)
+ }
}, [traceSpans])
useEffect(() => {
@@ -439,8 +524,6 @@ export function FrozenCanvas({
fetchData()
}, [executionId])
- // No need to create a temporary workflow - just use the workflowState directly
-
if (loading) {
return (
@@ -502,16 +585,18 @@ export function FrozenCanvas({
showSubBlocks={true}
isPannable={true}
onNodeClick={(blockId) => {
- if (blockExecutions[blockId]) {
- setPinnedBlockId(blockId)
- }
+ // Always allow clicking blocks, even if they don't have execution data
+ // This is important for failed workflows where some blocks never executed
+ setPinnedBlockId(blockId)
}}
/>
- {pinnedBlockId && blockExecutions[pinnedBlockId] && (
+ {pinnedBlockId && (
setPinnedBlockId(null)}
/>
)}
diff --git a/apps/sim/lib/logs/enhanced-execution-logger.ts b/apps/sim/lib/logs/enhanced-execution-logger.ts
index e32348b4a0..3917bfd4d7 100644
--- a/apps/sim/lib/logs/enhanced-execution-logger.ts
+++ b/apps/sim/lib/logs/enhanced-execution-logger.ts
@@ -218,8 +218,20 @@ export class EnhancedExecutionLogger implements IExecutionLoggerService {
logger.debug(`Completing workflow execution ${executionId}`)
- const level = 'info'
- const message = `Workflow execution completed`
+ // Determine if workflow failed by checking trace spans for errors
+ const hasErrors = traceSpans && traceSpans.some((span: any) => {
+ const checkSpanForErrors = (s: any): boolean => {
+ if (s.status === 'error') return true
+ if (s.children && Array.isArray(s.children)) {
+ return s.children.some(checkSpanForErrors)
+ }
+ return false
+ }
+ return checkSpanForErrors(span)
+ })
+
+ const level = hasErrors ? 'error' : 'info'
+ const message = hasErrors ? 'Workflow execution failed' : 'Workflow execution completed'
const [updatedLog] = await db
.update(workflowExecutionLogs)