Ema Workflows: Debugging Common Problems
Why isn't my email sending? Why does my AI Employee say the same thing three times? This debugging guide covers the most common workflow problems and shows you exactly how to diagnose and fix them—with real examples from production workflows.
The Debugging Mindset
Before diving into specific problems, understand the rules that govern workflow execution:
| Rule | Implication |
|---|---|
| Reachability | A node must have an execution path from trigger |
| Trigger gating | Classifier outputs must connect to trigger_when for conditional paths |
| Required inputs | All required inputs must be produced and connected |
| Dependency sync | If one upstream never fires, downstream nodes stall |
| Side effects need reachability | Email/API calls need both data AND trigger paths |
Keep these in mind as we diagnose specific issues.
Problem 1: Email Never Sends
This is the #1 support question. Your workflow has an email node, but emails never go out.
Cause 1A: Email Node is Disconnected (Unreachable)
Symptoms: Email node exists but never executes.
Diagnosis: Check if the node has an upstream path from trigger.
flowchart LR
subgraph connected["Connected Path ✓"]
T["Trigger"] --> C["Classifier"] --> A["Agent"] --> O["WORKFLOW_OUTPUT"]
end
subgraph disconnected["Disconnected ✗"]
FR["Fixed Response"] --> SE["Send Email"]
end
style disconnected fill:#ffcdd2,stroke:#c62828
Problem: The email node has inputs wired but no path from trigger activates it.
What happens: The email node has inputs wired (subject, to, body), but nothing in the graph ever "activates" it. There's no upstream path from trigger.
Fix:
- Connect a routing classifier output to the email node's
trigger_when - Or wire the email node into the main execution flow
# Before: No trigger path
send_email:
inputs:
subject: fixed_response.subject
to: fixed_response.to
body: fixed_response.body
# No trigger_when = never runs if disconnected
# After: Explicit routing
send_email:
trigger_when: chat_categorizer.category == "send-email"
inputs:
subject: construct_subject.response
to: construct_to.response
body: construct_body.response
Cause 1B: Missing Trigger Gating on Email Path
Symptoms: Email node is connected but still doesn't run.
Diagnosis: The email path depends on upstream nodes that are gated by categories that don't fire.
flowchart TD
CL["Classifier"]
CL -->|"category::identify_caller"| EE["Entity Extraction"]
EE --> CE["Construct To Email"]
CE --> SE["Send Email"]
CL -->|"category::generate-response"| OP["Other Path"]
style EE fill:#fff9c4
style CE fill:#fff9c4
style SE fill:#fff9c4
Problem: If classifier outputs "generate-response" but not "identify_caller", the email path never gets its to_email input.
Failure mode: If the routing classifier doesn't emit category::identify_caller for the conversation, entity extraction never runs → to_email is never populated → email send fails.
Fix:
- Don't gate user resolution behind a separate category if email always needs it
- Gate the ENTIRE email flow behind a single "send email" category
- Provide fallback email sources
# Option 1: Run user resolution unconditionally
entity_extraction:
# No trigger_when - always runs
inputs:
conversation: trigger.chat_conversation
# Option 2: Gate entire email flow together
send_email_flow:
trigger_when: classifier.category == "send-email"
# All email-related nodes share this gate
Cause 1C: Malformed Data in Email Fields
Symptoms: Email node runs but fails validation or sends garbage.
Diagnosis: Check what's being wired to subject, to_email, body.
Common mistake: Using response_with_sources instead of clean output
# ❌ BAD: response_with_sources may contain citations, metadata
send_email:
to_email: call_llm.response_with_sources
# Result: "The email is john@example.com [Source: client-db]"
# ✅ GOOD: Use clean response output
send_email:
to_email: call_llm.response
# Result: "john@example.com"
Better: Force structured JSON output
call_llm_email_address:
instructions: |
Extract the recipient email address.
Output ONLY valid JSON: {"email": "user@domain.com"}
No explanation, no sources, just JSON.
json_extractor:
input: call_llm_email_address.response
path: $.email
send_email:
to_email: json_extractor.email # Clean value
Email Debugging Checklist
| Check | What to Look For |
|---|---|
| 1. Reachability | Does the email node have a path from trigger? |
| 2. Trigger gating | Is trigger_when connected to the right category? |
| 3. Upstream dependencies | Do ALL upstream nodes (to, subject, body) actually run? |
| 4. Data format | Is to_email a clean email string, not response_with_sources? |
| 5. Required inputs | Are subject, to, body ALL populated? |
Problem 2: Agent Repeats the Same Thing 3 Times
Your AI Employee responds with the same message multiple times. This is almost always caused by multiple responder nodes running in parallel.
Cause 2A: Multiple Unguarded "Final Responders"
Symptoms: Every message produces 2-3 identical (or similar) responses.
Diagnosis: Check what feeds into WORKFLOW_OUTPUT:
flowchart LR
FH["Fallback Handler<br/>(no trigger_when)<br/>ALWAYS runs!"]
FR["Fallback Response<br/>(gated by Fallback)"]
SE["Send Email Handler<br/>(email_result)"]
WO["WORKFLOW_OUTPUT"]
FH --> WO
FR --> WO
SE --> WO
style FH fill:#ffcdd2
Result: Up to 3 outputs displayed as chat messages!
Key culprit: Ungated fallback handlers
If a node has no trigger_when and receives input from an always-running node (like conversation_summarizer), it runs on every single turn.
Fix:
- Gate ALL responder nodes with
trigger_when - Ensure only ONE path to
WORKFLOW_OUTPUTis active per turn
# ❌ BAD: No gate = always runs
fallback_handler:
inputs:
query: conversation_summarizer.summarized_conversation
# Runs every turn, even when classifier routes elsewhere!
# ✅ GOOD: Explicit gate
fallback_handler:
trigger_when: classifier.category == "general-question"
inputs:
query: conversation_summarizer.summarized_conversation
Cause 2B: Redundant Classifiers with Overlapping Categories
Symptoms: Different branches activate for the same input.
Diagnosis: Check if you have multiple classifiers analyzing the same input:
flowchart TD
T["Trigger"]
T --> IC1["Intent Classifier 1"]
IC1 -->|"Fallback"| FR1["Fallback Response 1"]
T --> CC["Chat Categorizer"]
CC -->|"generate-response"| RH["Response Handler"]
style IC1 fill:#ffcdd2
style CC fill:#ffcdd2
Problem: Both classifiers analyze same input independently. If input is ambiguous, BOTH downstream nodes run.
Fix: Consolidate into a single classifier with mutually exclusive categories:
# ❌ BAD: Two classifiers = potential overlap
intent_classifier:
categories: [Fallback, ...]
chat_categorizer:
categories: [generate-response, send-email, ...]
# ✅ GOOD: Single classifier with all categories
unified_classifier:
categories:
- SEND_EMAIL: "Requests to send communications"
- PORTFOLIO_REVIEW: "Questions about portfolio"
- FALLBACK: "Unclear or general requests"
# Only ONE category per turn = only ONE path runs
Cause 2C: Ungated Email Pipeline Producing Chat Output
Symptoms: Email confirmation appears as a chat response alongside the actual answer.
Diagnosis: The email pipeline runs when it shouldn't, and its output is rendered as chat.
# Problem nodes (no trigger_when):
construct_email_subject:
# No gate - always runs
construct_to_email:
# No gate - always runs
extract_email_body:
# No gate - always runs
send_email_handler:
# Runs when all inputs arrive
# Returns email_result that gets displayed
Fix:
- Gate email pipeline nodes behind "send-email" category
- Don't display
email_resultas chat message
# Gate the entire email pipeline
construct_email_subject:
trigger_when: classifier.category == "send-email"
send_email_handler:
trigger_when: classifier.category == "send-email"
Cause 2D: Trigger Fires Multiple Times Per Message
Symptoms: Entire workflow runs 2-3 times for one user message.
Diagnosis: Check trigger configuration—it may fire on:
- Message creation AND update
- Each speech-to-text chunk (partial transcripts)
- Retries after timeouts
Fix:
- Configure trigger for "final message only"
- Add idempotency by tracking processed message IDs
- If using speech-to-text, wait for final transcript
# Ensure trigger fires once per finalized message
chat_trigger:
fire_on: "message_finalized" # Not "message_created" + "message_updated"
dedupe_by: "message_id"
Cause 2E: Retry Behavior Duplicates Outputs
Symptoms: Same response appears multiple times, often with slight delays between them.
Diagnosis: Platform-level retries on LLM timeouts or errors.
Fix:
- Reduce retry count for chat-visible nodes
- Make side-effect actions idempotent (dedupe key for emails)
Repetition Debugging Checklist
| Check | What to Look For |
|---|---|
| 1. Multiple responders | How many nodes feed into WORKFLOW_OUTPUT? |
| 2. Ungated handlers | Does fallback_handler have a trigger_when? |
| 3. Overlapping classifiers | Are there multiple classifiers that could both fire? |
| 4. Email as chat output | Is email_result being displayed as a message? |
| 5. Trigger frequency | Does trigger fire once or multiple times per message? |
| 6. Retry settings | Are LLM nodes retrying and producing duplicate outputs? |
Problem 3: Workflow Gets Stuck (No Response)
Your AI Employee starts processing but never produces output.
Cause 3A: Dependency Deadlock
Symptoms: Workflow starts but never completes.
Diagnosis: A node waits for inputs that never arrive because upstream nodes are gated.
flowchart TD
A["Node A<br/>trigger_when: category == 'path-a'"]
B["Node B<br/>trigger_when: category == 'path-b'"]
C["Node C<br/>Waits for BOTH A and B"]
A -->|"input_1"| C
B -->|"input_2"| C
style B fill:#ffcdd2
style C fill:#fff9c4
Problem: If classifier outputs "path-a", Node B never runs. Node C waits forever for node_b.output.
Fix:
- Don't require inputs from mutually exclusive branches
- Use optional inputs or defaults
- Restructure to separate branches
Cause 3B: Unused Search Results (Dead Branch)
Symptoms: Workflow runs knowledge search but then does nothing with results.
Diagnosis: knowledge_search.search_results has no downstream consumers.
# Problem: Search runs but output goes nowhere
knowledge_search:
trigger_when: category == "generate-response"
inputs:
query: summarizer.output
# search_results not connected to anything!
# Fix: Connect results to consuming node
custom_agent:
named_inputs:
- SearchResults: knowledge_search.search_results # ← Add this
Problem 4: Loops and Infinite Execution
Your workflow runs endlessly or produces cascading outputs.
Cause: Cyclic Dependencies Without Exit Condition
Symptoms: Multiple executions, growing outputs, or timeout.
Diagnosis: Look for cycles in your node graph:
flowchart LR
A["Agent A"] --> FR["Fixed Response"] --> S["Search"]
S --> EE["Entity Extraction"]
EE --> A
style A fill:#ffcdd2
style EE fill:#ffcdd2
Problem: If nodes re-run on updated inputs, this loops forever.
Fix:
- Break the cycle—make resolution a one-pass step
- Add explicit exit conditions
- Ensure only one node produces the "final" value
Quick Diagnostic Reference
| Problem | Most Likely Cause | First Thing to Check |
|---|---|---|
| Email never sends | Node unreachable | Does email node have path from trigger? |
| Email fails validation | Malformed data | Is to_email clean or response_with_sources? |
| Triple response | Ungated handlers | Does fallback_handler have trigger_when? |
| Same response twice | Overlapping classifiers | Are there multiple classifiers? |
| Workflow stuck | Dependency deadlock | Are required inputs from gated nodes? |
| Infinite loop | Cyclic dependencies | Is there a cycle in the graph? |
Summary: The Debugging Rules
| Rule | Check |
|---|---|
| Reachability | Every node must have a path from trigger |
| Gating | Use trigger_when to make branches mutually exclusive |
| Single responder | Only ONE path should produce chat output per turn |
| Data mapping | Use clean outputs, not response_with_sources |
| Dependencies | Don't wait for inputs from gated nodes that might not run |
| Consolidation | Use ONE classifier, not multiple overlapping ones |
The golden rule: For any user message, exactly ONE path should execute and produce exactly ONE response. If multiple paths can run, you'll get multiple outputs. If no path runs, you'll get nothing.
For more on routing patterns, see Ema Workflows: Routing and Branching. For entity extraction issues, see Ema Workflows: Mastering Entity Extraction.