AI Agents LangGraph

Edges in LangGraph

Intermediate

Edges in LangGraph

In this topic, we explore edges in LangGraph and understand how they control execution flow between nodes in a graph-based workflow. We discuss why edges are important, how execution moves through a graph, and the difference between fixed edges and conditional edges.

We cover the major edge patterns used in LangGraph, including sequential flows, branching workflows, loops, parallel execution paths, and START/END connections. We also examine routing logic using router functions and the Command object for advanced dynamic workflows.

Additionally, we explore practical workflow patterns such as ReAct loops, fan-out/fan-in architectures, retry systems, human-in-the-loop flows, and hierarchical graph structures. Finally, we review common mistakes, debugging considerations, and best practices for designing clean, maintainable, and reliable LangGraph workflows before graph compilation.

Edges in LangGraph

In this topic we deep dive into edges in LangGraph.

What Are Edges in LangGraph?

Edges define the flow of execution between nodes — they tell the graph “where to go next” after a node finishes.In simple terms:
  • If Nodes are the “actions” or “workers”,
  • Then Edges are the “roads” or “connections” that link those actions together.
in a simple Analogy, think of nodes as cities and edges as highways. A fixed edge is a direct highway from City A to City B. A conditional edge is a roundabout with signs that say “Go to B if it’s sunny, go to C if it’s raining.”

There are two main types of Edges in LangGraph:

  1. Fixed / Static Edges (add_edge)
    • Always go from one node to another specific node.
    • Used for predictable, linear flows.
  2. Conditional / Dynamic Edges (add_conditional_edges)
    • Decide the next node at runtime (usually based on LLM output or logic).
    • Most powerful and commonly used in agents.

The others:

  • cyclic edges
  • parallel edges
  • START/END edges

are more like:

  • workflow patterns
  • execution behaviors
  • architectural uses of edges

rather than separate LangGraph edge classes. In this topic all are covered. 


Why Edges Matter

In LangGraph, nodes do not execute in the order you add them. They execute only according to the edges you define.This is one of the most important concepts to understand.Why This Matters:
  • Execution Flow is Explicit: The graph’s behavior is determined by the connections (edges), not by the sequence in which you wrote the code. This makes your workflow logic clear and predictable.
  • Flexibility in Design: You can add nodes in any order you like. The actual execution path is defined separately through edges. This allows you to easily reorganize, reuse, or modify flows.
  • Supports Complex Architectures: Edges enable powerful patterns like:
    • Branching (conditional routing)
    • Loops (agent cycles)
    • Parallel execution
    • Merging paths
    • Skipping or repeating steps
  • Easier Debugging & Visualization: When something goes wrong, you can look at the edges to instantly understand how data flows through your graph — instead of hunting through code order.
Important Reminder:
Adding a node doesn’t automatically connect it to anything. Until you add edges, your nodes are isolated islands.
 
 

 

How Edges Work in LangGraph

Edges are the connectors that define the execution path in your graph. After a node finishes running, LangGraph looks at the edges to decide which node should run next.
 
Core Concept
Every time a node completes, it returns updates to the state. Then LangGraph uses the edges coming out of that node to determine the next step(s).LangGraph supports two main types of edges:
 

1. Fixed (Static) Edges – add_edge()

These always go from one specific node to another. Used for predictable, linear flows.
from langgraph.graph import StateGraph, START, END

graph = StateGraph(MessagesState)

graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)
graph.add_node("summarize", summarize_node)

# Fixed edges
graph.add_edge(START, "agent")
graph.add_edge("agent", "tools")
graph.add_edge("tools", "summarize")
graph.add_edge("summarize", END)

2. Conditional (Dynamic) Edges – add_conditional_edges()

These decide the next node at runtime, usually based on the output of the current node (especially LLM decisions).
def route_after_agent(state: MessagesState):
    last_message = state["messages"][-1]
    
    if last_message.tool_calls:
        return "tools"        # Go to tool node
    else:
        return "END"          # Finish

# Add conditional edge
graph.add_conditional_edges(
    "agent",                  # Starting node
    route_after_agent,        # Router function
    {
        "tools": "tools",     # Mapping: return value → target node
        "END": END
    }
)
 
How Execution Actually Flows
  1. Graph starts at START
  2. A node runs
  3. LangGraph checks all outgoing edges from that node
  4. It moves to the next node(s) based on those edges
  5. Repeats until it reaches END
Key Point:
A node can have multiple outgoing edges, enabling branching, parallel execution, and loops.
 

 

Execution Flow in LangGraph

Execution Flow refers to the step-by-step process LangGraph follows when running your graph — from the starting point all the way to the end.Unlike regular Python code that runs line by line, LangGraph executes based on the nodes and edges you defined.How Execution Works (Step by Step)
  1. Start
    The graph always begins at the special START node.
  2. Node Execution
    The current node receives the latest state, runs its logic, and returns updates.
  3. State Update
    LangGraph merges the node’s return value into the current state (using reducers where needed).
  4. Edge Evaluation
    LangGraph looks at all outgoing edges from the current node to decide what to do next:
    • Fixed edge → Go to the specified node
    • Conditional edge → Run the router function to decide the next node
  5. Repeat
    Steps 2–4 continue until the graph reaches the special END node (or an interrupt occurs).
  6. Finish
    When END is reached, execution stops and the final state is returned.
The execution flow is completely driven by edges, not by the order in which we wrote our code. This gives you powerful control over branching, looping, parallelism, and human-in-the-loop workflows.


Types of Edges

LangGraph supports several types of edges that give you full control over how your workflow executes. Here’s a breakdown of each type:
 

Normal Edges (Fixed / Static Edges)

Normal edges create a fixed, predictable path from one node to another. They are the simplest and most straightforward type of edge.
  • Linear workflows
  • Straightforward step-by-step processes
  • When the next step is always the same
 
graph.add_edge(START, "agent")
graph.add_edge("agent", "tools")
graph.add_edge("tools", "summarize")
graph.add_edge("summarize", END)
 
Key Point: Once defined, the flow never changes — always goes from source node to target node.


Conditional Edges (Dynamic Routing)

Conditional edges decide the next node at runtime based on logic or the output of the current node. This is the most powerful and commonly used edge type in agents.When to use:
  • Tool calling decisions
  • Branching logic
  • Different workflows based on user input or LLM output
def route_after_agent(state):
    if state["messages"][-1].tool_calls:
        return "tools"
    return "END"

graph.add_conditional_edges(
    "agent",                    # Source node
    route_after_agent,          # Router function
    {
        "tools": "tools",       # Mapping: return value → target node
        "END": END
    }
)
Pro Tip: Most real-world agents rely heavily on conditional edges. We cover this edge in more details in another topic in Conditional Edges.


Cyclic Edges (Loops)

Cyclic edges allow the graph to loop back to a previous node, enabling repeated execution until a condition is met.When to use:
  • ReAct-style agents
  • Retry mechanisms
  • Multi-step reasoning loops
# Example: Agent keeps running until it decides to finish
graph.add_conditional_edges(
    "agent",
    route_after_agent,
    {
        "tools": "tools",
        "continue": "agent",   # ← Loop back to agent
        "END": END
    }
)

graph.add_edge("tools", "agent")   # Tool result goes back to agent
Key Point: This is what makes LangGraph perfect for building intelligent agents that can think multiple steps.

Parallel Edges

Parallel edges allow multiple nodes to run simultaneously. LangGraph automatically waits for all parallel branches to complete before continuing.When to use:
  • Running multiple searches at once
  • Concurrent tool calls
  • Multi-source data gathering
# Fan-out from START
graph.add_edge(START, "web_search")
graph.add_edge(START, "vector_search")

# Fan-in to aggregator
graph.add_edge("web_search", "aggregate")
graph.add_edge("vector_search", "aggregate")
Modern alternative using SEND:
from langgraph.types import Send

def route_parallel(state):
    return [
        Send("web_search", state),
        Send("vector_search", state)
    ]

graph.add_conditional_edges(START, route_parallel)

 


START/END Edges

START and END are built-in virtual nodes in LangGraph.
  • START: The entry point of every graph. You always connect from START.
  • END: The exit point. When the graph reaches END, execution stops.
from langgraph.graph import START, END

graph.add_edge(START, "first_node")
graph.add_edge("last_node", END)

# You can also use them in conditional edges
graph.add_conditional_edges("router", lambda s: "END" if done else "next_node")
Important:
  • We cannot add a node named START or END.
  • Every graph must eventually reach END (unless using interrupts).


Routing Logic

Routing Logic is the decision-making process that determines which node should run next after the current node finishes. It is primarily used with Conditional Edges.In LangGraph, routing logic is what makes your graphs intelligent and dynamic — allowing them to branch, loop, or take different paths based on conditions.
 
Why Routing Logic Matters
  • Enables dynamic workflows instead of rigid linear flows
  • Allows the graph to respond differently based on LLM output, state, or custom rules
  • Essential for building agents that can decide whether to call tools, continue reasoning, or finish
How Routing Logic Works
We define routing logic using a router function that:
  1. Takes the current state as input
  2. Returns the name of the next node (as a string) or a list of nodes (for parallel execution)
LangGraph then uses this return value to follow the correct edge.
 
Simple Rule-Based Router
def route_tools(state: MessagesState) -> str:
    """Simple routing based on tool calls."""
    last_message = state["messages"][-1]
    
    if last_message.tool_calls:
        return "tools"          # Go to Tool Node
    else:
        return "END"            # Finish execution

 

LLM-Powered Router (Most Common in Agents)
def route_after_agent(state: MessagesState):
    """Let the LLM decide the next step via tool calling."""
    last_message = state["messages"][-1]
    
    # If LLM requested tool calls
    if last_message.tool_calls:
        return "tools"
    
    # Otherwise, end the conversation
    return "END"


# Attach the router to a conditional edge
graph.add_conditional_edges(
    "agent", 
    route_after_agent,
    {
        "tools": "tools",
        "END": END
    }
)

 

Advanced Router with Multiple Conditions
def complex_router(state: MessagesState):
    last_message = state["messages"][-1]
    confidence = getattr(last_message, "confidence", 0.0)
    
    if "error" in state:
        return "fix_error"
    elif confidence < 0.7:
        return "retry"
    elif last_message.tool_calls:
        return "tools"
    else:
        return "summarize"

 

Using the Command Object for Advanced Routing

The Command object (introduced in newer versions of LangGraph) is a powerful way to combine state updates and routing logic inside a single node. Instead of separating routing into a dedicated router function + conditional edges, you can return a Command directly from any node. This makes your graphs cleaner, more readable, and easier to manage — especially for complex or dynamic workflows.Why Use Command?
  • Combines state update + next destination in one return
  • Reduces the need for many conditional edges
  • Great for dynamic routing, multi-agent handoffs, and edgeless graphs
  • More intuitive when the decision and update belong together
from langgraph.types import Command
from langgraph.graph import StateGraph, START, END
from typing import Literal

def router_node(state):
    """Decide next step and update state in one go."""
    user_input = state["messages"][-1].content.lower()
    
    if "joke" in user_input:
        return Command(
            update={"intent": "joke"},           # Update state
            goto="joke_node"                     # Route to next node
        )
    elif "research" in user_input:
        return Command(
            update={"intent": "research"},
            goto="research_node"
        )
    else:
        return Command(
            update={"intent": "unknown"},
            goto=END
        )


# Add the node (no conditional edge needed!)
graph.add_node("router", router_node)
graph.add_node("joke_node", joke_generator)
graph.add_node("research_node", researcher)

graph.add_edge(START, "router")
Advanced Example with Type Hints (for better graph visualization)
from langgraph.types import Command
from typing_extensions import Literal

def agent_node(state) -> Command[Literal["tools", "END"]]:
    """LLM-powered agent that returns Command."""
    
    response = llm_with_tools.invoke(state["messages"])
    
    if response.tool_calls:
        return Command(
            update={"messages": [response]},   # Add the LLM message
            goto="tools"                       # Route to tools
        )
    else:
        return Command(
            update={"messages": [response]},
            goto=END
        )
When to prefer Command over traditional conditional edges?
  • When the routing decision and state update are tightly coupled
  • In multi-agent systems or complex branching logic
  • When you want cleaner, more maintainable code

Best Practices for Routing Logic

  • Keep router functions pure and fast (avoid heavy computation)
  • Make return values match the keys in your add_conditional_edges() mapping
  • Use clear, consistent node names
  • Handle all possible cases to avoid errors
  • Consider using Command object (newer LangGraph feature) for more advanced control

Routing Logic + Conditional Edges = The brain of a LangGraph application. This is where simple scripts become intelligent, adaptive AI workflows.

 


add_edge()

Creates a direct, always-on connection from one node to another. The flow is predictable and never changes.
Syntax:
graph.add_edge(source: str, target: str)
Common Usage:
from langgraph.graph import StateGraph, START, END

graph = StateGraph(MessagesState)

graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)
graph.add_node("summarize", summarize_node)

# Fixed edges
graph.add_edge(START, "agent")
graph.add_edge("agent", "tools")
graph.add_edge("tools", "summarize")
graph.add_edge("summarize", END)
When to use add_edge():
  • Linear workflows
  • Predictable step-by-step processes
  • Connecting to START and END
  • Simple pipelines
Key Characteristics:
  • Simple and readable
  • No runtime decision needed
  • Cannot branch or loop by itself

 

add_conditional_edges()

Creates a smart edge that decides the next node at runtime based on the current state.This is the most important method for building intelligent agents.
Syntax:
graph.add_conditional_edges(
    source: str,                    # Starting node
    path: Callable,                 # Router function
    path_map: dict | None = None,   # Mapping: return value → target node
    then: str | None = None         # Optional: run this node after all paths
)
Complete Example:
def route_after_agent(state: MessagesState) -> str:
    """Router function - decides where to go next"""
    last_message = state["messages"][-1]
    
    if last_message.tool_calls:
        return "tools"
    elif "continue" in last_message.content.lower():
        return "agent"      # Loop back
    else:
        return "END"


# Add conditional edge
graph.add_conditional_edges(
    "agent",                    # Source node
    route_after_agent,          # Router function
    {
        "tools": "tools",       # If router returns "tools" → go to tools node
        "agent": "agent",       # Loop back to agent
        "END": END              # Finish execution
    }
)

 

Pro Tips:
  • Use add_edge() for simple, predictable connections.
  • Use add_conditional_edges() whenever the next step depends on logic or LLM output.
  • You can mix both types in the same graph.
  • Keep your router functions fast and pure (avoid heavy computation).

Branching Workflows in LangGraph

Branching Workflows allow your graph to take different paths based on conditions, decisions, or LLM outputs. Instead of following a single linear path, the graph can split into multiple routes, making your AI application much more intelligent and flexible.
This is one of the most powerful features of LangGraph.
 

Why Branching Matters

  • Handle different types of user requests
  • Implement if-this-then-that logic
  • Support fallback paths
  • Create dynamic multi-step reasoning
  • Build complex agent behaviors

 

How to Create Branching Workflows
 
You create branches primarily using add_conditional_edges().
 

Simple Branching Example (Rule-based)

def route_query(state: MessagesState) -> str:
    """Decide which branch to take based on user input."""
    user_input = state["messages"][-1].content.lower()
    
    if "joke" in user_input:
        return "joke_branch"
    elif "research" in user_input or "search" in user_input:
        return "research_branch"
    elif "math" in user_input or "calculate" in user_input:
        return "calculator_branch"
    else:
        return "general_branch"


# Add the branching
graph.add_conditional_edges(
    "agent",                    # Start branching from agent node
    route_query,
    {
        "joke_branch": "joke_node",
        "research_branch": "research_node",
        "calculator_branch": "calculator_node",
        "general_branch": "general_response_node"
    }
)

 

LLM-Powered Branching (Most Common)

def route_after_agent(state: MessagesState):
    """Let the LLM decide the best branch using tool calling."""
    last_message = state["messages"][-1]
    
    if last_message.tool_calls:
        return "tools"                    # Tool calling branch
    elif "analyze" in last_message.content.lower():
        return "analysis_branch"
    else:
        return "final_answer"


graph.add_conditional_edges(
    "agent",
    route_after_agent,
    {
        "tools": "tools",
        "analysis_branch": "data_analyzer",
        "final_answer": "final_answer_node"
    }
)

 

Multiple Branches from One Node (Fan-out)

We can also send execution to multiple branches at the same time using Send:
from langgraph.types import Send

def route_to_parallel_branches(state):
    return [
        Send("web_search", state),
        Send("vector_search", state),
        Send("news_search", state)
    ]

graph.add_conditional_edges(START, route_to_parallel_branches)

 

Best Practices for Branching Workflows:
  • Keep router functions simple and fast
  • Always handle all possible return values
  • Use clear, descriptive branch names
  • Consider using the Command object for cleaner code in complex cases
  • Test each branch independently
Branching workflows turn a simple chain into a smart decision engine. This is what separates basic scripts from production-grade AI agents.

 

Loops and Cycles in LangGraph

Loops (or Cycles) are one of the most powerful features of LangGraph. They allow the graph to return to a previous node and execute it again — enabling repeated reasoning, tool calling, retries, and multi-step problem solving.
This is what makes LangGraph ideal for building agents (like ReAct agents) that can think in loops until they solve a task.
 
Why Loops Matter
  • Allow agents to iterate and improve their answers
  • Support retry logic on failure
  • Enable multi-turn reasoning
  • Make it possible to keep calling tools until the task is complete
  • Create persistent, stateful workflows
How to Create Loops in LangGraph
We create loops by directing an edge back to a previous node, usually using conditional edges.
 

Basic Loop Pattern (ReAct Style)

from langgraph.graph import StateGraph, START, END

def route_after_agent(state: MessagesState) -> str:
    last_message = state["messages"][-1]
    
    if last_message.tool_calls:
        return "tools"           # Go to tools
    else:
        return "END"             # Finish


graph = StateGraph(MessagesState)

graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)

graph.add_edge(START, "agent")

# Create the loop
graph.add_conditional_edges("agent", route_after_agent)

# After tools finish, loop back to agent
graph.add_edge("tools", "agent")

graph.add_edge("agent", END)   # Only reached via conditional
Flow:
START → Agent → (decides)
          ├── Tools → back to Agent (loop)
          └── END (when done)

Loop with Maximum Iterations (Safety)

def route_with_limit(state: MessagesState) -> str:
    # Count how many times agent has run
    agent_runs = sum(1 for msg in state["messages"] if isinstance(msg, AIMessage))
    
    if agent_runs > 10:                    # Safety limit
        return "END"
    elif state["messages"][-1].tool_calls:
        return "tools"
    else:
        return "END"

Using Command for Cleaner Loops

from langgraph.types import Command

def agent_node(state):
    response = llm_with_tools.invoke(state["messages"])
    
    if response.tool_calls:
        return Command(
            update={"messages": [response]},
            goto="tools"           # Continue loop
        )
    else:
        return Command(
            update={"messages": [response]},
            goto=END
        )
 
Important Tips:
  • Always add a maximum iteration limit to prevent infinite loops
  • Use checkpointer (persistence) so the graph can resume after interruptions
  • Keep loop logic clear and readable
  • Monitor state size — especially message history (use summarization if needed)

Loops turn a static graph into a thinking, iterating agent. Combined with conditional edges and state management, this is where LangGraph really shines.

 

Returning END in LangGraph

END is a special built-in node in LangGraph that signals the end of execution. Once the graph reaches END, it stops running and returns the final state to the user.
Understanding when and how to return END is crucial for controlling your workflow.
 
What Does END Mean?
  • It tells LangGraph: “This workflow is complete. Stop here.”
  • No further nodes will execute after END.
  • The compiled graph will return the current state as the final output.

Ways to Return END

Using Fixed Edge (Simple)

from langgraph.graph import StateGraph, START, END

graph.add_edge("final_node", END)   # Always end after this node

Using Conditional Edges (Most Common)

def route_after_agent(state: MessagesState) -> str:
    last_message = state["messages"][-1]
    
    # Decide to finish
    if not last_message.tool_calls and "final answer" in last_message.content.lower():
        return "END"                    # ← Return END as string
    elif last_message.tool_calls:
        return "tools"
    else:
        return "agent"


graph.add_conditional_edges(
    "agent",
    route_after_agent,
    {
        "tools": "tools",
        "agent": "agent",
        "END": END          # ← Map the string "END" to the special END node
    }
)
from langgraph.types import Command

def agent_node(state):
    response = llm_with_tools.invoke(state["messages"])
    
    if response.tool_calls:
        return Command(update={"messages": [response]}, goto="tools")
    else:
        return Command(update={"messages": [response]}, goto=END)  # Direct END

Best Practices for Returning END

 

  • Be explicit — Always define a clear condition for when the graph should end.
  • Use meaningful checks like:
    • No more tool calls
    • Final answer is generated
    • Task is completed
    • Confidence threshold reached
  • Add safety limits (max iterations) to prevent infinite loops.
  • Return END from a router function or directly via Command.

Example of a good ending condition:

 

if last_message.tool_calls:
    return "tools"
elif "final_answer" in state:          # Custom state flag
    return "END"
else:
    return "agent"
END is the stop sign. Properly returning END ensures our graph doesn’t run forever and gives clean, predictable completion.

 

Edge Design Patterns in LangGraph

Mastering edge design patterns helps you build cleaner, more maintainable, and scalable workflows. Here are the most common and powerful patterns used in real-world LangGraph applications.

Linear / Sequential Pattern

graph.add_edge(START, "preprocess")
graph.add_edge("preprocess", "agent")
graph.add_edge("agent", "postprocess")
graph.add_edge("postprocess", END)
Use when: Simple pipelines, data processing flows.
 
graph.add_edge(START, "agent")

graph.add_conditional_edges(
    "agent",
    route_after_agent,
    {"tools": "tools", "END": END}
)

graph.add_edge("tools", "agent")   # Loop back
Classic flow: Agent → Tools → Agent → Tools → ... → END
 

Branching / Router Pattern

One node routes to multiple possible paths.
graph.add_conditional_edges(
    "router",
    intent_classifier,
    {
        "research": "research_node",
        "creative": "creative_node",
        "math": "calculator_node",
        "general": "general_node"
    }
)
 

Parallel + Aggregation Pattern (Fan-Out / Fan-In)

Run multiple operations at the same time, then combine results.
# Fan-out
graph.add_edge("start", "web_search")
graph.add_edge("start", "vector_search")
graph.add_edge("start", "news_search")

# Fan-in
graph.add_edge("web_search", "aggregate")
graph.add_edge("vector_search", "aggregate")
graph.add_edge("news_search", "aggregate")

 

Hierarchical / Subgraph Pattern

Use a subgraph as a single node with internal edges.
research_team = create_research_subgraph().compile()
graph.add_node("research_team", research_team)   # Subgraph as node

graph.add_edge("planner", "research_team")
graph.add_edge("research_team", "final_answer")
 

Human-in-the-Loop Pattern

Pause execution and wait for human feedback.
graph.add_conditional_edges(
    "agent",
    lambda s: "human_approval" if needs_approval(s) else END
)

# Interrupt before human review
app = graph.compile(
    checkpointer=checkpointer,
    interrupt_before=["human_approval"]
)

 

Self-Correcting / Retry Pattern

Loop back on failure or low confidence.
def route_with_retry(state):
    if state.get("error") or confidence_low(state):
        return "fix_node"      # or back to "agent"
    return END

graph.add_conditional_edges("validator", route_with_retry)
graph.add_edge("fix_node", "agent")   # Retry
 

Command-Based Dynamic Routing (Modern Pattern)

from langgraph.types import Command

def supervisor_node(state):
    if condition:
        return Command(update=..., goto="worker_a")
    else:
        return Command(update=..., goto="worker_b")

 

Common Mistakes When Working with Nodes & Edges in LangGraph

Even experienced developers make these mistakes. Here are the most frequent pitfalls and how to avoid them.

 

1. Forgetting to Add Edges

Mistake:
graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)
# Forgot to connect them!
Problem: Nodes are added but isolated → graph does nothing.
Fix: Always connect nodes whit  add_edge() or add_conditional_edges().
 

2. Incorrect Router Return Values

Mistake:
def bad_router(state):
    if condition:
        return "tool_node"        # Wrong name!
    return END

Problem: Router returns a string that doesn’t match any node name.

Fix: Make sure the router’s return values exactly match the keys in your path_map.

 4. Missing END Condition (Infinite Loops)

Mistake: Creating a loop without a proper exit condition.
Fix: Always include a clear stopping condition:
if max_iterations_reached(state) or task_complete(state):
    return END

 

5. Using Same Node Name Twice

Mistake: Adding two nodes with the same name.
Fix: Use unique, descriptive names (snake_case).
 

6. Not Handling All Router Cases

Mistake:
def router(state):
    if tool_calls:
        return "tools"
    # What if neither condition is true?
Fix: Always cover all possible paths:
return "tools" if ... else "END"

 

7. Confusing add_edge() with add_conditional_edges()

  •  Use add_edge() → for fixed flows
  • Use add_conditional_edges() → for decisions

8. Forgetting to Import START and END

Mistake:
graph.add_edge("start", "agent")   # Wrong!
Fix:
from langgraph.graph import START, END
graph.add_edge(START, "agent")
graph.add_edge("final", END)

 

Quick Checklist Before Compiling

  • All nodes are connected via edges?
  • Router functions return valid node names?
  • Every loop has an exit condition?
  • Using partial state updates?
  • Imported START and END?
  • Tested with graph.compile()

 

 

 

AI agent LangChain LangGraph Python

← All training