UNPKG

@agentled/cli

Version:

CLI for Agentled — manage workflows, apps, and knowledge from the command line. Zero context-window cost for AI agents.

158 lines (121 loc) 5.21 kB
# 07 — Error handling: skip vs stop vs wait **Problem**: Developers use the same error behavior (`stop` or `skip`) everywhere, causing workflows to either halt on recoverable errors or continue silently with missing data. **Why it fails silently**: `skip` silently passes empty/null data to downstream steps, which then produce wrong outputs with no error. `stop` halts workflows on optional steps that should have been bypassed. Neither produces a visible failure at the wrong step the failure surfaces later, in a confusing place. --- ## The three behaviors Every entry condition has an `onCriteriaFail` setting that determines what happens when the condition isn't met: | Value | What it does | When to use | |---|---|---| | `skip` | Skip this step, pass `null`/empty to downstream steps, continue execution | Optional step workflow is valid without it | | `stop` | Halt the entire execution immediately | Hard prerequisite nothing downstream is meaningful without this step | | `wait` | Block this step until criteria become true | Async dependency waiting for a loop or parallel group to finish | --- ## Anti-pattern: stop everywhere ```yaml # Wrong: using stop for an optional enrichment step - id: enrich-linkedin type: app-action entryConditions: onCriteriaFail: "stop" # ← wrong: this is optional criteria: - variable: "{{input.linkedinUrl}}" operator: "isNotNull" # If linkedinUrl is missing: entire workflow halts. # But the workflow could run fine without LinkedIn data. ``` ```yaml # Correct: optional enrichment step uses skip - id: enrich-linkedin type: app-action entryConditions: onCriteriaFail: "skip" # ← correct: skip if no URL conditionText: "Skip LinkedIn enrichment if no URL provided" criteria: - variable: "{{input.linkedinUrl}}" operator: "isNotNull" ``` --- ## Anti-pattern: skip for hard prerequisites ```yaml # Wrong: using skip for required input validation - id: validate-required-fields type: code entryConditions: onCriteriaFail: "skip" # ← wrong: nothing downstream works without this criteria: - variable: "{{input.companyDomain}}" operator: "isNotNull" # If domain is missing: validation is skipped. # Next step tries to enrich with null domain. # Enrichment fails with a confusing error 3 steps later. ``` ```yaml # Correct: hard prerequisite uses stop - id: validate-required-fields type: code entryConditions: onCriteriaFail: "stop" # ← correct: stop early with a clear failure conditionText: "Company domain is required" criteria: - variable: "{{input.companyDomain}}" operator: "isNotNull" ``` Fail fast. A clear stop at the prerequisite is better than a confusing error 5 steps later. --- ## `wait`: blocking on async completion `wait` is for a specific use case: blocking a step until an async process finishes. The two common cases are loop completion and parallel group completion. ```yaml # Wait for a loop to finish before consuming its output - id: aggregate-results type: ai-action entryConditions: onCriteriaFail: "wait" conditionText: "Wait for all enrichment loop iterations to complete" criteria: - type: loop_completion stepId: enrich-loop operator: "==" value: true prompt: "Summarize these enriched companies: {{steps.enrich-loop.outputs}}" ``` `wait` is not a general-purpose retry mechanism. It's specifically for dependency ordering in async workflows. --- ## Decision guide Ask these questions in order: **1. Is this step required for the workflow to produce a valid output?** - Yes `stop` - No continue to question 2 **2. Is this step waiting for another step/loop/group to finish?** - Yes `wait` - No `skip` Examples: | Step | Required? | Async wait? | Use | |---|---|---|---| | "Validate required domain field" | Yes | No | `stop` | | "Enrich LinkedIn (optional)" | No | No | `skip` | | "Send Slack alert (if webhook configured)" | No | No | `skip` | | "Aggregate loop results" | Yes | Yes (loop) | `wait` | | "Score company (ICP match required)" | Yes | No | `stop` | | "Add CRM note (nice to have)" | No | No | `skip` | --- ## Handling downstream null data after skip When a step is skipped, its outputs are `null` or empty. Downstream steps that reference those outputs must handle nulls gracefully: ```yaml # Pattern: null-safe reference in AI prompt prompt: | Company: {{steps.enrich.company.name | default: "Unknown"}} LinkedIn headline: {{steps.enrich-linkedin.headline | default: "Not available"}} Score this company based on what's available. ``` Or use a code step to normalize before passing to downstream AI steps: ```javascript // Code step: normalize potentially-null enrichment data return { name: input.enrichData?.name ?? input.rawInput.companyName, industry: input.enrichData?.industry ?? "Unknown", employees: input.enrichData?.employees ?? null, }; ``` --- ## One-line rule > Use `skip` for optional steps, `stop` for hard prerequisites, and `wait` for async dependencies and always handle `null` outputs from skipped steps in downstream steps.