fix(conditional): don't error in condition blocks when no conditions are satisfied (#2243)

* fix(conditional): don't error in condition blocks when no conditions are satisfied

* updated docs
This commit is contained in:
Waleed
2025-12-08 00:15:51 -08:00
committed by GitHub
parent d09fd6cf92
commit e2b077f582
3 changed files with 32 additions and 13 deletions

View File

@@ -135,7 +135,7 @@ Function (Process) → Condition (account_type === 'enterprise') → Advanced or
## Best Practices
- **Order conditions correctly**: Place more specific conditions before general ones to ensure specific logic takes precedence over fallbacks
- **Include a default condition**: Add a catch-all condition (`true`) as the last condition to handle unmatched cases and prevent workflow execution from getting stuck
- **Use the else branch when needed**: If no conditions match and the else branch is not connected, the workflow branch will end gracefully. Connect the else branch if you need a fallback path for unmatched cases
- **Keep expressions simple**: Use clear, straightforward boolean expressions for better readability and easier debugging
- **Document your conditions**: Add descriptions to explain the purpose of each condition for better team collaboration and maintenance
- **Test edge cases**: Verify conditions handle boundary values correctly by testing with values at the edges of your condition ranges

View File

@@ -365,7 +365,7 @@ describe('ConditionBlockHandler', () => {
)
})
it('should throw error if no condition matches and no else exists', async () => {
it('should return no-match result if no condition matches and no else exists', async () => {
const conditions = [
{ id: 'cond1', title: 'if', value: 'false' },
{ id: 'cond2', title: 'else if', value: 'context.value === 99' },
@@ -392,9 +392,15 @@ describe('ConditionBlockHandler', () => {
.mockReturnValueOnce('false')
.mockReturnValueOnce('context.value === 99')
await expect(handler.execute(mockContext, mockBlock, inputs)).rejects.toThrow(
`No matching path found for condition block "${mockBlock.metadata?.name}", and no 'else' block exists.`
)
const result = await handler.execute(mockContext, mockBlock, inputs)
// Should return success with no path selected (branch ends gracefully)
expect((result as any).conditionResult).toBe(false)
expect((result as any).selectedPath).toBeNull()
expect((result as any).selectedConditionId).toBeNull()
expect((result as any).selectedOption).toBeNull()
// Decision should not be set when no condition matches
expect(mockContext.decisions.condition.has(mockBlock.id)).toBe(false)
})
it('falls back to else path when loop context data is unavailable', async () => {

View File

@@ -87,6 +87,17 @@ export class ConditionBlockHandler implements BlockHandler {
block
)
// Handle case where no condition matched and no else exists - branch ends gracefully
if (!selectedConnection || !selectedCondition) {
return {
...((sourceOutput as any) || {}),
conditionResult: false,
selectedPath: null,
selectedConditionId: null,
selectedOption: null,
}
}
const targetBlock = ctx.workflow?.blocks.find((b) => b.id === selectedConnection?.target)
if (!targetBlock) {
throw new Error(`Target block ${selectedConnection?.target} not found`)
@@ -145,8 +156,8 @@ export class ConditionBlockHandler implements BlockHandler {
ctx: ExecutionContext,
block: SerializedBlock
): Promise<{
selectedConnection: { target: string; sourceHandle?: string }
selectedCondition: { id: string; title: string; value: string }
selectedConnection: { target: string; sourceHandle?: string } | null
selectedCondition: { id: string; title: string; value: string } | null
}> {
for (const condition of conditions) {
if (condition.title === CONDITION.ELSE_TITLE) {
@@ -185,14 +196,16 @@ export class ConditionBlockHandler implements BlockHandler {
if (elseConnection) {
return { selectedConnection: elseConnection, selectedCondition: elseCondition }
}
throw new Error(
`No path found for condition block "${block.metadata?.name}", and 'else' connection missing.`
)
// Else exists but has no connection - treat as no match, branch ends
logger.info(`No condition matched and else has no connection - branch ending`, {
blockId: block.id,
})
return { selectedConnection: null, selectedCondition: null }
}
throw new Error(
`No matching path found for condition block "${block.metadata?.name}", and no 'else' block exists.`
)
// No condition matched and no else exists - branch ends gracefully
logger.info(`No condition matched and no else block - branch ending`, { blockId: block.id })
return { selectedConnection: null, selectedCondition: null }
}
private findConnectionForCondition(