Skip to content

Commit b6c2c74

Browse files
authored
fix(variables): fix variable resolution error and start block outputs (#1847)
* Fix var resolution if block is not upstream * Filter convo id from start block outputs if not set * Lint * Start block outputs
1 parent 9fd2156 commit b6c2c74

File tree

4 files changed

+112
-62
lines changed

4 files changed

+112
-62
lines changed

apps/sim/executor/execution/block-executor.ts

Lines changed: 85 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,21 @@ export class BlockExecutor {
7171

7272
try {
7373
resolvedInputs = this.resolver.resolveInputs(ctx, node.id, block.config.params, block)
74-
} finally {
74+
} catch (error) {
7575
cleanupSelfReference?.()
76+
return this.handleBlockError(
77+
error,
78+
ctx,
79+
node,
80+
block,
81+
startTime,
82+
blockLog,
83+
resolvedInputs,
84+
isSentinel,
85+
'input_resolution'
86+
)
7687
}
88+
cleanupSelfReference?.()
7789

7890
try {
7991
const output = handler.executeWithNode
@@ -120,58 +132,17 @@ export class BlockExecutor {
120132

121133
return normalizedOutput
122134
} catch (error) {
123-
const duration = Date.now() - startTime
124-
const errorMessage = normalizeError(error)
125-
126-
if (blockLog) {
127-
blockLog.endedAt = new Date().toISOString()
128-
blockLog.durationMs = duration
129-
blockLog.success = false
130-
blockLog.error = errorMessage
131-
}
132-
133-
const errorOutput: NormalizedBlockOutput = {
134-
error: errorMessage,
135-
}
136-
137-
this.state.setBlockOutput(node.id, errorOutput, duration)
138-
139-
logger.error('Block execution failed', {
140-
blockId: node.id,
141-
blockType: block.metadata?.id,
142-
error: errorMessage,
143-
})
144-
145-
if (!isSentinel) {
146-
this.callOnBlockComplete(ctx, node, block, resolvedInputs, errorOutput, duration)
147-
}
148-
149-
const hasErrorPort = this.hasErrorPortEdge(node)
150-
151-
if (hasErrorPort) {
152-
logger.info('Block has error port - returning error output instead of throwing', {
153-
blockId: node.id,
154-
error: errorMessage,
155-
})
156-
return errorOutput
157-
}
158-
159-
let errorToThrow: Error | string
160-
if (error instanceof Error) {
161-
errorToThrow = error
162-
} else {
163-
errorToThrow = errorMessage
164-
}
165-
166-
throw buildBlockExecutionError({
135+
return this.handleBlockError(
136+
error,
137+
ctx,
138+
node,
167139
block,
168-
error: errorToThrow,
169-
context: ctx,
170-
additionalInfo: {
171-
nodeId: node.id,
172-
executionTime: duration,
173-
},
174-
})
140+
startTime,
141+
blockLog,
142+
resolvedInputs,
143+
isSentinel,
144+
'execution'
145+
)
175146
}
176147
}
177148

@@ -196,6 +167,68 @@ export class BlockExecutor {
196167
return this.blockHandlers.find((h) => h.canHandle(block))
197168
}
198169

170+
private handleBlockError(
171+
error: unknown,
172+
ctx: ExecutionContext,
173+
node: DAGNode,
174+
block: SerializedBlock,
175+
startTime: number,
176+
blockLog: BlockLog | undefined,
177+
resolvedInputs: Record<string, any>,
178+
isSentinel: boolean,
179+
phase: 'input_resolution' | 'execution'
180+
): NormalizedBlockOutput {
181+
const duration = Date.now() - startTime
182+
const errorMessage = normalizeError(error)
183+
184+
if (blockLog) {
185+
blockLog.endedAt = new Date().toISOString()
186+
blockLog.durationMs = duration
187+
blockLog.success = false
188+
blockLog.error = errorMessage
189+
}
190+
191+
const errorOutput: NormalizedBlockOutput = {
192+
error: errorMessage,
193+
}
194+
195+
this.state.setBlockOutput(node.id, errorOutput, duration)
196+
197+
logger.error(
198+
phase === 'input_resolution' ? 'Failed to resolve block inputs' : 'Block execution failed',
199+
{
200+
blockId: node.id,
201+
blockType: block.metadata?.id,
202+
error: errorMessage,
203+
}
204+
)
205+
206+
if (!isSentinel) {
207+
this.callOnBlockComplete(ctx, node, block, resolvedInputs, errorOutput, duration)
208+
}
209+
210+
const hasErrorPort = this.hasErrorPortEdge(node)
211+
if (hasErrorPort) {
212+
logger.info('Block has error port - returning error output instead of throwing', {
213+
blockId: node.id,
214+
error: errorMessage,
215+
})
216+
return errorOutput
217+
}
218+
219+
const errorToThrow = error instanceof Error ? error : new Error(errorMessage)
220+
221+
throw buildBlockExecutionError({
222+
block,
223+
error: errorToThrow,
224+
context: ctx,
225+
additionalInfo: {
226+
nodeId: node.id,
227+
executionTime: duration,
228+
},
229+
})
230+
}
231+
199232
private hasErrorPortEdge(node: DAGNode): boolean {
200233
for (const [_, edge] of node.outgoingEdges) {
201234
if (edge.sourceHandle === EDGE.ERROR) {

apps/sim/executor/utils/start-block.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ describe('start-block utilities', () => {
7171
})
7272

7373
expect(output.payload).toBe('value')
74-
expect(output.input).toBe('')
75-
expect(output.conversationId).toBe('')
74+
expect(output.input).toBeUndefined()
75+
expect(output.conversationId).toBeUndefined()
7676
})
7777

7878
it('buildStartBlockOutput uses trigger schema for API triggers', () => {
@@ -101,8 +101,6 @@ describe('start-block utilities', () => {
101101
size: 42,
102102
type: 'text/plain',
103103
key: 'file-key',
104-
uploadedAt: new Date().toISOString(),
105-
expiresAt: new Date(Date.now() + 1000).toISOString(),
106104
},
107105
]
108106

apps/sim/executor/utils/start-block.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,25 @@ function buildUnifiedStartOutput(workflowInput: unknown): NormalizedBlockOutput
266266
}
267267

268268
if (!Object.hasOwn(output, 'input')) {
269-
output.input = ''
269+
const fallbackInput =
270+
isPlainObject(workflowInput) && typeof workflowInput.input !== 'undefined'
271+
? ensureString(workflowInput.input)
272+
: ''
273+
output.input = fallbackInput ? fallbackInput : undefined
274+
} else if (typeof output.input === 'string' && output.input.length === 0) {
275+
output.input = undefined
270276
}
277+
271278
if (!Object.hasOwn(output, 'conversationId')) {
272-
output.conversationId = ''
279+
const conversationId =
280+
isPlainObject(workflowInput) && workflowInput.conversationId
281+
? ensureString(workflowInput.conversationId)
282+
: undefined
283+
if (conversationId) {
284+
output.conversationId = conversationId
285+
}
286+
} else if (typeof output.conversationId === 'string' && output.conversationId.length === 0) {
287+
output.conversationId = undefined
273288
}
274289

275290
return mergeFilesIntoOutput(output, workflowInput)
@@ -293,7 +308,11 @@ function buildChatOutput(workflowInput: unknown): NormalizedBlockOutput {
293308

294309
const output: NormalizedBlockOutput = {
295310
input: ensureString(source?.input),
296-
conversationId: ensureString(source?.conversationId),
311+
}
312+
313+
const conversationId = ensureString(source?.conversationId)
314+
if (conversationId) {
315+
output.conversationId = conversationId
297316
}
298317

299318
return mergeFilesIntoOutput(output, workflowInput)
@@ -319,7 +338,7 @@ function buildLegacyStarterOutput(
319338
}
320339

321340
const conversationId = isPlainObject(workflowInput) ? workflowInput.conversationId : undefined
322-
if (conversationId !== undefined) {
341+
if (conversationId) {
323342
output.conversationId = ensureString(conversationId)
324343
}
325344

apps/sim/executor/variables/resolvers/block.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ export class BlockResolver implements Resolver {
4343

4444
const output = this.getBlockOutput(blockId, context)
4545

46-
if (!output) {
47-
throw new Error(`No state found for block "${blockName}"`)
46+
if (output === undefined) {
47+
return undefined
4848
}
4949
if (pathParts.length === 0) {
5050
return output

0 commit comments

Comments
 (0)