[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C5W2CES04Q0CFM911XYDT8":3,"skill-same:01M6C5W2CES04Q0CFM911XYDT8":53},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":27,"linkOnly":28,"markdownContent":29,"markdownBody":30,"frontMatter":31,"refName":32,"commitSha":33,"skillDirectory":34,"forksCount":14,"repositoryHtmlUrl":35,"repositoryHomepageUrl":6,"ownerHtmlUrl":36,"ownerBlogUrl":37,"ownerDescription":6,"updatedAt":38,"install":39,"resources":52},"01M6C5W2CES04Q0CFM911XYDT8","langgraph-fundamentals",null,"INVOKE THIS SKILL when writing ANY LangGraph code. Covers StateGraph, state schemas, nodes, edges, Command, Send, invoke, streaming, and error handling.","CoralShades\u002Facm-ai","CoralShades","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F220297585?v=4","01M6C5211QSJHZ8HXSDDWCR507","Coral Shades AI Agency","01M6C52161FNQDRQ0FRR8DAA9D",0,"MIT","https:\u002F\u002Fgithub.com\u002FCoralShades\u002Facm-ai\u002Fblob\u002Fmain\u002F.continue\u002Fskills\u002Flanggraph-fundamentals\u002FSKILL.md",[],[19,20,21,22,23,24,25,26],"langgraph","langgraph fundamentals","writing","code","error handling","state","streaming","command","2026-11-10 10:12:30",false,"---\nname: langgraph-fundamentals\ndescription: \"INVOKE THIS SKILL when writing ANY LangGraph code. Covers StateGraph, state schemas, nodes, edges, Command, Send, invoke, streaming, and error handling.\"\n---\n\n\u003Coverview>\nLangGraph models agent workflows as **directed graphs**:\n\n- **StateGraph**: Main class for building stateful graphs\n- **Nodes**: Functions that perform work and update state\n- **Edges**: Define execution order (static or conditional)\n- **START\u002FEND**: Special nodes marking entry and exit points\n- **State with Reducers**: Control how state updates are merged\n\nGraphs must be `compile()`d before execution.\n\u003C\u002Foverview>\n\n\u003Cdesign-methodology>\n\n### Designing a LangGraph application\n\nFollow these 5 steps when building a new graph:\n\n1. **Map out discrete steps** — sketch a flowchart of your workflow. Each step becomes a node.\n2. **Identify what each step does** — categorize nodes: LLM step, data step, action step, or user input step. For each, determine static context (prompt), dynamic context (from state), retry strategy, and desired outcome.\n3. **Design your state** — state is shared memory for all nodes. Store raw data, format prompts on-demand inside nodes.\n4. **Build your nodes** — implement each step as a function that takes state and returns partial updates.\n5. **Wire it together** — connect nodes with edges, add conditional routing, compile with a checkpointer if needed.\n\n\u003C\u002Fdesign-methodology>\n\n\u003Cwhen-to-use-langgraph>\n\n| Use LangGraph When | Use Alternatives When |\n|-------------------|----------------------|\n| Need fine-grained control over agent orchestration | Quick prototyping → LangChain agents |\n| Building complex workflows with branching\u002Floops | Simple stateless workflows → LangChain direct |\n| Require human-in-the-loop, persistence | Batteries-included features → Deep Agents |\n\n\u003C\u002Fwhen-to-use-langgraph>\n\n---\n\n## State Management\n\n\u003Cstate-update-strategies>\n\n| Need | Solution | Example |\n|------|----------|---------|\n| Overwrite value | No reducer (default) | Simple fields like counters |\n| Append to list | Reducer (operator.add \u002F concat) | Message history, logs |\n| Custom logic | Custom reducer function | Complex merging |\n\n\u003C\u002Fstate-update-strategies>\n\n\u003Cex-state-with-reducer>\n\u003Cpython>\nDefine state schema with reducers for accumulating lists and summing integers.\n```python\nfrom typing_extensions import TypedDict, Annotated\nimport operator\n\nclass State(TypedDict):\n    name: str  # Default: overwrites on update\n    messages: Annotated[list, operator.add]  # Appends to list\n    total: Annotated[int, operator.add]  # Sums integers\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse StateSchema with ReducedValue for accumulating arrays.\n```typescript\nimport { StateSchema, ReducedValue, MessagesValue } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  name: z.string(),  \u002F\u002F Default: overwrites\n  messages: MessagesValue,  \u002F\u002F Built-in for messages\n  items: new ReducedValue(\n    z.array(z.string()).default(() => []),\n    { reducer: (current, update) => current.concat(update) }\n  ),\n});\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-state-with-reducer>\n\n\u003Cfix-forgot-reducer-for-list>\n\u003Cpython>\nWithout a reducer, returning a list overwrites previous values.\n```python\n# WRONG: List will be OVERWRITTEN\nclass State(TypedDict):\n    messages: list  # No reducer!\n\n# Node 1 returns: {\"messages\": [\"A\"]}\n# Node 2 returns: {\"messages\": [\"B\"]}\n# Final: {\"messages\": [\"B\"]}  # \"A\" is LOST!\n\n# CORRECT: Use Annotated with operator.add\nfrom typing import Annotated\nimport operator\n\nclass State(TypedDict):\n    messages: Annotated[list, operator.add]\n# Final: {\"messages\": [\"A\", \"B\"]}\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nWithout ReducedValue, arrays are overwritten not appended.\n```typescript\n\u002F\u002F WRONG: Array will be overwritten\nconst State = new StateSchema({\n  items: z.array(z.string()),  \u002F\u002F No reducer!\n});\n\u002F\u002F Node 1: { items: [\"A\"] }, Node 2: { items: [\"B\"] }\n\u002F\u002F Final: { items: [\"B\"] }  \u002F\u002F A is lost!\n\n\u002F\u002F CORRECT: Use ReducedValue\nconst State = new StateSchema({\n  items: new ReducedValue(\n    z.array(z.string()).default(() => []),\n    { reducer: (current, update) => current.concat(update) }\n  ),\n});\n\u002F\u002F Final: { items: [\"A\", \"B\"] }\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-forgot-reducer-for-list>\n\n\u003Cfix-state-must-return-dict>\n\u003Cpython>\nNodes must return partial updates, not mutate and return full state.\n```python\n# WRONG: Returning entire state object\ndef my_node(state: State) -> State:\n    state[\"field\"] = \"updated\"\n    return state  # Don't mutate and return!\n\n# CORRECT: Return dict with only the updates\ndef my_node(state: State) -> dict:\n    return {\"field\": \"updated\"}\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nReturn partial updates only, not the full state object.\n```typescript\n\u002F\u002F WRONG: Returning entire state\nconst myNode = async (state: typeof State.State) => {\n  state.field = \"updated\";\n  return state;  \u002F\u002F Don't do this!\n};\n\n\u002F\u002F CORRECT: Return partial updates\nconst myNode = async (state: typeof State.State) => {\n  return { field: \"updated\" };\n};\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-state-must-return-dict>\n\n---\n\n## Nodes\n\n\u003Cnode-function-signatures>\n\nNode functions accept these arguments:\n\n\u003Cpython>\n\n| Signature | When to Use |\n|-----------|-------------|\n| `def node(state: State)` | Simple nodes that only need state |\n| `def node(state: State, config: RunnableConfig)` | Need thread_id, tags, or configurable values |\n| `def node(state: State, runtime: Runtime[Context])` | Need runtime context, store, or stream_writer |\n\n```python\nfrom langchain_core.runnables import RunnableConfig\nfrom langgraph.runtime import Runtime\n\ndef plain_node(state: State):\n    return {\"results\": \"done\"}\n\ndef node_with_config(state: State, config: RunnableConfig):\n    thread_id = config[\"configurable\"][\"thread_id\"]\n    return {\"results\": f\"Thread: {thread_id}\"}\n\ndef node_with_runtime(state: State, runtime: Runtime[Context]):\n    user_id = runtime.context.user_id\n    return {\"results\": f\"User: {user_id}\"}\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\n\n| Signature | When to Use |\n|-----------|-------------|\n| `(state) => {...}` | Simple nodes that only need state |\n| `(state, config) => {...}` | Need thread_id, tags, or configurable values |\n\n```typescript\nimport { GraphNode, StateSchema } from \"@langchain\u002Flanggraph\";\n\nconst plainNode: GraphNode\u003Ctypeof State> = (state) => {\n  return { results: \"done\" };\n};\n\nconst nodeWithConfig: GraphNode\u003Ctypeof State> = (state, config) => {\n  const threadId = config?.configurable?.thread_id;\n  return { results: `Thread: ${threadId}` };\n};\n```\n\u003C\u002Ftypescript>\n\n\u003C\u002Fnode-function-signatures>\n\n---\n\n## Edges\n\n\u003Cedge-type-selection>\n\n| Need | Edge Type | When to Use |\n|------|-----------|-------------|\n| Always go to same node | `add_edge()` | Fixed, deterministic flow |\n| Route based on state | `add_conditional_edges()` | Dynamic branching |\n| Update state AND route | `Command` | Combine logic in single node |\n| Fan-out to multiple nodes | `Send` | Parallel processing with dynamic inputs |\n\n\u003C\u002Fedge-type-selection>\n\n\u003Cex-basic-graph>\n\u003Cpython>\nSimple two-node graph with linear edges.\n```python\nfrom langgraph.graph import StateGraph, START, END\nfrom typing_extensions import TypedDict\n\nclass State(TypedDict):\n    input: str\n    output: str\n\ndef process_input(state: State) -> dict:\n    return {\"output\": f\"Processed: {state['input']}\"}\n\ndef finalize(state: State) -> dict:\n    return {\"output\": state[\"output\"].upper()}\n\ngraph = (\n    StateGraph(State)\n    .add_node(\"process\", process_input)\n    .add_node(\"finalize\", finalize)\n    .add_edge(START, \"process\")\n    .add_edge(\"process\", \"finalize\")\n    .add_edge(\"finalize\", END)\n    .compile()\n)\n\nresult = graph.invoke({\"input\": \"hello\"})\nprint(result[\"output\"])  # \"PROCESSED: HELLO\"\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nChain nodes with addEdge and compile before invoking.\n```typescript\nimport { StateGraph, StateSchema, START, END } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  input: z.string(),\n  output: z.string().default(\"\"),\n});\n\nconst processInput = async (state: typeof State.State) => {\n  return { output: `Processed: ${state.input}` };\n};\n\nconst finalize = async (state: typeof State.State) => {\n  return { output: state.output.toUpperCase() };\n};\n\nconst graph = new StateGraph(State)\n  .addNode(\"process\", processInput)\n  .addNode(\"finalize\", finalize)\n  .addEdge(START, \"process\")\n  .addEdge(\"process\", \"finalize\")\n  .addEdge(\"finalize\", END)\n  .compile();\n\nconst result = await graph.invoke({ input: \"hello\" });\nconsole.log(result.output);  \u002F\u002F \"PROCESSED: HELLO\"\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-basic-graph>\n\n\u003Cex-conditional-edges>\n\u003Cpython>\nRoute to different nodes based on state with conditional edges.\n```python\nfrom typing import Literal\nfrom langgraph.graph import StateGraph, START, END\n\nclass State(TypedDict):\n    query: str\n    route: str\n    result: str\n\ndef classify(state: State) -> dict:\n    if \"weather\" in state[\"query\"].lower():\n        return {\"route\": \"weather\"}\n    return {\"route\": \"general\"}\n\ndef route_query(state: State) -> Literal[\"weather\", \"general\"]:\n    return state[\"route\"]\n\ngraph = (\n    StateGraph(State)\n    .add_node(\"classify\", classify)\n    .add_node(\"weather\", lambda s: {\"result\": \"Sunny, 72F\"})\n    .add_node(\"general\", lambda s: {\"result\": \"General response\"})\n    .add_edge(START, \"classify\")\n    .add_conditional_edges(\"classify\", route_query, [\"weather\", \"general\"])\n    .add_edge(\"weather\", END)\n    .add_edge(\"general\", END)\n    .compile()\n)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\naddConditionalEdges routes based on function return value.\n```typescript\nimport { StateGraph, StateSchema, START, END } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  query: z.string(),\n  route: z.string().default(\"\"),\n  result: z.string().default(\"\"),\n});\n\nconst classify = async (state: typeof State.State) => {\n  if (state.query.toLowerCase().includes(\"weather\")) {\n    return { route: \"weather\" };\n  }\n  return { route: \"general\" };\n};\n\nconst routeQuery = (state: typeof State.State) => state.route;\n\nconst graph = new StateGraph(State)\n  .addNode(\"classify\", classify)\n  .addNode(\"weather\", async () => ({ result: \"Sunny, 72F\" }))\n  .addNode(\"general\", async () => ({ result: \"General response\" }))\n  .addEdge(START, \"classify\")\n  .addConditionalEdges(\"classify\", routeQuery, [\"weather\", \"general\"])\n  .addEdge(\"weather\", END)\n  .addEdge(\"general\", END)\n  .compile();\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-conditional-edges>\n\n---\n\n## Command\n\nCommand combines state updates and routing in a single return value. Fields:\n- **`update`**: State updates to apply (like returning a dict from a node)\n- **`goto`**: Node name(s) to navigate to next\n- **`resume`**: Value to resume after `interrupt()` — see human-in-the-loop skill\n\n\u003Cex-command-state-and-routing>\n\u003Cpython>\nCommand lets you update state AND choose next node in one return.\n```python\nfrom langgraph.types import Command\nfrom typing import Literal\n\nclass State(TypedDict):\n    count: int\n    result: str\n\ndef node_a(state: State) -> Command[Literal[\"node_b\", \"node_c\"]]:\n    \"\"\"Update state AND decide next node in one return.\"\"\"\n    new_count = state[\"count\"] + 1\n    if new_count > 5:\n        return Command(update={\"count\": new_count}, goto=\"node_c\")\n    return Command(update={\"count\": new_count}, goto=\"node_b\")\n\ngraph = (\n    StateGraph(State)\n    .add_node(\"node_a\", node_a)\n    .add_node(\"node_b\", lambda s: {\"result\": \"B\"})\n    .add_node(\"node_c\", lambda s: {\"result\": \"C\"})\n    .add_edge(START, \"node_a\")\n    .add_edge(\"node_b\", END)\n    .add_edge(\"node_c\", END)\n    .compile()\n)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nReturn Command with update and goto to combine state change with routing.\n```typescript\nimport { StateGraph, StateSchema, START, END, Command } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  count: z.number().default(0),\n  result: z.string().default(\"\"),\n});\n\nconst nodeA = async (state: typeof State.State) => {\n  const newCount = state.count + 1;\n  if (newCount > 5) {\n    return new Command({ update: { count: newCount }, goto: \"node_c\" });\n  }\n  return new Command({ update: { count: newCount }, goto: \"node_b\" });\n};\n\nconst graph = new StateGraph(State)\n  .addNode(\"node_a\", nodeA, { ends: [\"node_b\", \"node_c\"] })\n  .addNode(\"node_b\", async () => ({ result: \"B\" }))\n  .addNode(\"node_c\", async () => ({ result: \"C\" }))\n  .addEdge(START, \"node_a\")\n  .addEdge(\"node_b\", END)\n  .addEdge(\"node_c\", END)\n  .compile();\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-command-state-and-routing>\n\n\u003Ccommand-return-type-annotations>\n\n**Python**: Use `Command[Literal[\"node_a\", \"node_b\"]]` as the return type annotation to declare valid goto destinations.\n\n**TypeScript**: Pass `{ ends: [\"node_a\", \"node_b\"] }` as the third argument to `addNode` to declare valid goto destinations.\n\n\u003C\u002Fcommand-return-type-annotations>\n\n\u003Cwarning-command-static-edges>\n\n**Warning**: `Command` only adds **dynamic** edges — static edges defined with `add_edge` \u002F `addEdge` still execute. If `node_a` returns `Command(goto=\"node_c\")` and you also have `graph.add_edge(\"node_a\", \"node_b\")`, **both** `node_b` and `node_c` will run.\n\n\u003C\u002Fwarning-command-static-edges>\n\n---\n\n## Send API\n\nFan-out with `Send`: return `[Send(\"worker\", {...})]` from a conditional edge to spawn parallel workers. Requires a reducer on the results field.\n\n\u003Cex-orchestrator-worker>\n\u003Cpython>\nFan out tasks to parallel workers using the Send API and aggregate results.\n```python\nfrom langgraph.types import Send\nfrom typing import Annotated\nimport operator\n\nclass OrchestratorState(TypedDict):\n    tasks: list[str]\n    results: Annotated[list, operator.add]\n    summary: str\n\ndef orchestrator(state: OrchestratorState):\n    \"\"\"Fan out tasks to workers.\"\"\"\n    return [Send(\"worker\", {\"task\": task}) for task in state[\"tasks\"]]\n\ndef worker(state: dict) -> dict:\n    return {\"results\": [f\"Completed: {state['task']}\"]}\n\ndef synthesize(state: OrchestratorState) -> dict:\n    return {\"summary\": f\"Processed {len(state['results'])} tasks\"}\n\ngraph = (\n    StateGraph(OrchestratorState)\n    .add_node(\"worker\", worker)\n    .add_node(\"synthesize\", synthesize)\n    .add_conditional_edges(START, orchestrator, [\"worker\"])\n    .add_edge(\"worker\", \"synthesize\")\n    .add_edge(\"synthesize\", END)\n    .compile()\n)\n\nresult = graph.invoke({\"tasks\": [\"Task A\", \"Task B\", \"Task C\"]})\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nFan out tasks to parallel workers using the Send API and aggregate results.\n```typescript\nimport { Send, StateGraph, StateSchema, ReducedValue, START, END } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  tasks: z.array(z.string()),\n  results: new ReducedValue(\n    z.array(z.string()).default(() => []),\n    { reducer: (curr, upd) => curr.concat(upd) }\n  ),\n  summary: z.string().default(\"\"),\n});\n\nconst orchestrator = (state: typeof State.State) => {\n  return state.tasks.map((task) => new Send(\"worker\", { task }));\n};\n\nconst worker = async (state: { task: string }) => {\n  return { results: [`Completed: ${state.task}`] };\n};\n\nconst synthesize = async (state: typeof State.State) => {\n  return { summary: `Processed ${state.results.length} tasks` };\n};\n\nconst graph = new StateGraph(State)\n  .addNode(\"worker\", worker)\n  .addNode(\"synthesize\", synthesize)\n  .addConditionalEdges(START, orchestrator, [\"worker\"])\n  .addEdge(\"worker\", \"synthesize\")\n  .addEdge(\"synthesize\", END)\n  .compile();\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-orchestrator-worker>\n\n\u003Cfix-send-accumulator>\n\u003Cpython>\nUse a reducer to accumulate parallel worker results (otherwise last worker overwrites).\n```python\n# WRONG: No reducer - last worker overwrites\nclass State(TypedDict):\n    results: list\n\n# CORRECT\nclass State(TypedDict):\n    results: Annotated[list, operator.add]  # Accumulates\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse ReducedValue to accumulate parallel worker results.\n```typescript\n\u002F\u002F WRONG: No reducer\nconst State = new StateSchema({ results: z.array(z.string()) });\n\n\u002F\u002F CORRECT\nconst State = new StateSchema({\n  results: new ReducedValue(z.array(z.string()).default(() => []), { reducer: (curr, upd) => curr.concat(upd) }),\n});\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-send-accumulator>\n\n---\n\n## Running Graphs: Invoke and Stream\n\n\u003Cinvoke-basics>\n\nCall `graph.invoke(input, config)` to run a graph to completion and return the final state.\n\n\u003Cpython>\n```python\nresult = graph.invoke({\"input\": \"hello\"})\n# With config (for persistence, tags, etc.)\nresult = graph.invoke({\"input\": \"hello\"}, {\"configurable\": {\"thread_id\": \"1\"}})\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\n```typescript\nconst result = await graph.invoke({ input: \"hello\" });\n\u002F\u002F With config\nconst result = await graph.invoke({ input: \"hello\" }, { configurable: { thread_id: \"1\" } });\n```\n\u003C\u002Ftypescript>\n\n\u003C\u002Finvoke-basics>\n\n\u003Cstream-mode-selection>\n\n| Mode | What it Streams | Use Case |\n|------|----------------|----------|\n| `values` | Full state after each step | Monitor complete state |\n| `updates` | State deltas | Track incremental updates |\n| `messages` | LLM tokens + metadata | Chat UIs |\n| `custom` | User-defined data | Progress indicators |\n\n\u003C\u002Fstream-mode-selection>\n\n\u003Cex-stream-llm-tokens>\n\u003Cpython>\nStream LLM tokens in real-time for chat UI display.\n```python\nfor chunk in graph.stream(\n    {\"messages\": [HumanMessage(\"Hello\")]},\n    stream_mode=\"messages\"\n):\n    token, metadata = chunk\n    if hasattr(token, \"content\"):\n        print(token.content, end=\"\", flush=True)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nStream LLM tokens in real-time for chat UI display.\n```typescript\nfor await (const chunk of graph.stream(\n  { messages: [new HumanMessage(\"Hello\")] },\n  { streamMode: \"messages\" }\n)) {\n  const [token, metadata] = chunk;\n  if (token.content) {\n    process.stdout.write(token.content);\n  }\n}\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-stream-llm-tokens>\n\n\u003Cex-stream-custom-data>\n\u003Cpython>\nEmit custom progress updates from within nodes using the stream writer.\n```python\nfrom langgraph.config import get_stream_writer\n\ndef my_node(state):\n    writer = get_stream_writer()\n    writer(\"Processing step 1...\")\n    # Do work\n    writer(\"Complete!\")\n    return {\"result\": \"done\"}\n\nfor chunk in graph.stream({\"data\": \"test\"}, stream_mode=\"custom\"):\n    print(chunk)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nEmit custom progress updates from within nodes using the stream writer.\n```typescript\nimport { getWriter } from \"@langchain\u002Flanggraph\";\n\nconst myNode = async (state: typeof State.State) => {\n  const writer = getWriter();\n  writer(\"Processing step 1...\");\n  \u002F\u002F Do work\n  writer(\"Complete!\");\n  return { result: \"done\" };\n};\n\nfor await (const chunk of graph.stream({ data: \"test\" }, { streamMode: \"custom\" })) {\n  console.log(chunk);\n}\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-stream-custom-data>\n\n---\n\n## Error Handling\n\nMatch the error type to the right handler:\n\n\u003Cerror-handling-table>\n\n| Error Type | Who Fixes | Strategy | Example |\n|---|---|---|---|\n| Transient (network, rate limits) | System | `RetryPolicy(max_attempts=3)` | `add_node(..., retry_policy=...)` |\n| LLM-recoverable (tool failures) | LLM | `ToolNode(tools, handle_tool_errors=True)` | Error returned as ToolMessage |\n| User-fixable (missing info) | Human | `interrupt({\"message\": ...})` | Collect missing data (see HITL skill) |\n| Unexpected | Developer | Let bubble up | `raise` |\n\n\u003C\u002Ferror-handling-table>\n\n\u003Cex-retry-policy>\n\u003Cpython>\nUse RetryPolicy for transient errors (network issues, rate limits).\n```python\nfrom langgraph.types import RetryPolicy\n\nworkflow.add_node(\n    \"search_documentation\",\n    search_documentation,\n    retry_policy=RetryPolicy(max_attempts=3, initial_interval=1.0)\n)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse retryPolicy for transient errors.\n```typescript\nworkflow.addNode(\n  \"searchDocumentation\",\n  searchDocumentation,\n  {\n    retryPolicy: { maxAttempts: 3, initialInterval: 1.0 },\n  },\n);\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-retry-policy>\n\n\u003Cex-tool-node-error-handling>\n\u003Cpython>\nUse ToolNode from langgraph.prebuilt to handle tool execution and errors. When handle_tool_errors=True, errors are returned as ToolMessages so the LLM can recover.\n```python\nfrom langgraph.prebuilt import ToolNode\n\ntool_node = ToolNode(tools, handle_tool_errors=True)\n\nworkflow.add_node(\"tools\", tool_node)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse ToolNode from @langchain\u002Flanggraph\u002Fprebuilt to handle tool execution and errors. When handleToolErrors is true, errors are returned as ToolMessages so the LLM can recover.\n```typescript\nimport { ToolNode } from \"@langchain\u002Flanggraph\u002Fprebuilt\";\n\nconst toolNode = new ToolNode(tools, { handleToolErrors: true });\n\nworkflow.addNode(\"tools\", toolNode);\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-tool-node-error-handling>\n\n---\n\n## Common Fixes\n\n\u003Cfix-compile-before-execution>\n\u003Cpython>\nMust compile() to get executable graph.\n```python\n# WRONG\nbuilder.invoke({\"input\": \"test\"})  # AttributeError!\n\n# CORRECT\ngraph = builder.compile()\ngraph.invoke({\"input\": \"test\"})\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nMust compile() to get executable graph.\n```typescript\n\u002F\u002F WRONG\nawait builder.invoke({ input: \"test\" });\n\n\u002F\u002F CORRECT\nconst graph = builder.compile();\nawait graph.invoke({ input: \"test\" });\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-compile-before-execution>\n\n\u003Cfix-infinite-loop-needs-exit>\n\u003Cpython>\nProvide conditional path to END to avoid infinite loops.\n```python\n# WRONG: Loops forever\nbuilder.add_edge(\"node_a\", \"node_b\")\nbuilder.add_edge(\"node_b\", \"node_a\")\n\n# CORRECT\ndef should_continue(state):\n    return END if state[\"count\"] > 10 else \"node_b\"\nbuilder.add_conditional_edges(\"node_a\", should_continue)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse conditional edges with END return to break loops.\n```typescript\n\u002F\u002F WRONG: Loops forever\nbuilder.addEdge(\"node_a\", \"node_b\").addEdge(\"node_b\", \"node_a\");\n\n\u002F\u002F CORRECT\nbuilder.addConditionalEdges(\"node_a\", (state) => state.count > 10 ? END : \"node_b\");\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-infinite-loop-needs-exit>\n\n\u003Cfix-common-mistakes>\nOther common mistakes:\n```python\n# Router must return names of nodes that exist in the graph\nbuilder.add_node(\"my_node\", func)  # Add node BEFORE referencing in edges\nbuilder.add_conditional_edges(\"node_a\", router, [\"my_node\"])\n\n# Command return type needs Literal for routing destinations (Python)\ndef node_a(state) -> Command[Literal[\"node_b\", \"node_c\"]]:\n    return Command(goto=\"node_b\")\n\n# START is entry-only - cannot route back to it\nbuilder.add_edge(\"node_a\", START)  # WRONG!\nbuilder.add_edge(\"node_a\", \"entry\")  # Use a named entry node instead\n\n# Reducer expects matching types\nreturn {\"items\": [\"item\"]}  # List for list reducer, not a string\n```\n```typescript\n\u002F\u002F Always await graph.invoke() - it returns a Promise\nconst result = await graph.invoke({ input: \"test\" });\n\n\u002F\u002F TS Command nodes need { ends } to declare routing destinations\nbuilder.addNode(\"router\", routerFn, { ends: [\"node_b\", \"node_c\"] });\n```\n\u003C\u002Ffix-common-mistakes>\n\n\u003Cboundaries>\n### What You Should NOT Do\n\n- Mutate state directly — always return partial update dicts from nodes\n- Route back to START — it's entry-only; use a named node instead\n- Forget reducers on list fields — without one, last write wins\n- Mix static edges with Command goto without understanding both will execute\n\u003C\u002Fboundaries>\n","\u003Coverview>\nLangGraph models agent workflows as **directed graphs**:\n\n- **StateGraph**: Main class for building stateful graphs\n- **Nodes**: Functions that perform work and update state\n- **Edges**: Define execution order (static or conditional)\n- **START\u002FEND**: Special nodes marking entry and exit points\n- **State with Reducers**: Control how state updates are merged\n\nGraphs must be `compile()`d before execution.\n\u003C\u002Foverview>\n\n\u003Cdesign-methodology>\n\n### Designing a LangGraph application\n\nFollow these 5 steps when building a new graph:\n\n1. **Map out discrete steps** — sketch a flowchart of your workflow. Each step becomes a node.\n2. **Identify what each step does** — categorize nodes: LLM step, data step, action step, or user input step. For each, determine static context (prompt), dynamic context (from state), retry strategy, and desired outcome.\n3. **Design your state** — state is shared memory for all nodes. Store raw data, format prompts on-demand inside nodes.\n4. **Build your nodes** — implement each step as a function that takes state and returns partial updates.\n5. **Wire it together** — connect nodes with edges, add conditional routing, compile with a checkpointer if needed.\n\n\u003C\u002Fdesign-methodology>\n\n\u003Cwhen-to-use-langgraph>\n\n| Use LangGraph When | Use Alternatives When |\n|-------------------|----------------------|\n| Need fine-grained control over agent orchestration | Quick prototyping → LangChain agents |\n| Building complex workflows with branching\u002Floops | Simple stateless workflows → LangChain direct |\n| Require human-in-the-loop, persistence | Batteries-included features → Deep Agents |\n\n\u003C\u002Fwhen-to-use-langgraph>\n\n---\n\n## State Management\n\n\u003Cstate-update-strategies>\n\n| Need | Solution | Example |\n|------|----------|---------|\n| Overwrite value | No reducer (default) | Simple fields like counters |\n| Append to list | Reducer (operator.add \u002F concat) | Message history, logs |\n| Custom logic | Custom reducer function | Complex merging |\n\n\u003C\u002Fstate-update-strategies>\n\n\u003Cex-state-with-reducer>\n\u003Cpython>\nDefine state schema with reducers for accumulating lists and summing integers.\n```python\nfrom typing_extensions import TypedDict, Annotated\nimport operator\n\nclass State(TypedDict):\n    name: str  # Default: overwrites on update\n    messages: Annotated[list, operator.add]  # Appends to list\n    total: Annotated[int, operator.add]  # Sums integers\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse StateSchema with ReducedValue for accumulating arrays.\n```typescript\nimport { StateSchema, ReducedValue, MessagesValue } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  name: z.string(),  \u002F\u002F Default: overwrites\n  messages: MessagesValue,  \u002F\u002F Built-in for messages\n  items: new ReducedValue(\n    z.array(z.string()).default(() => []),\n    { reducer: (current, update) => current.concat(update) }\n  ),\n});\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-state-with-reducer>\n\n\u003Cfix-forgot-reducer-for-list>\n\u003Cpython>\nWithout a reducer, returning a list overwrites previous values.\n```python\n# WRONG: List will be OVERWRITTEN\nclass State(TypedDict):\n    messages: list  # No reducer!\n\n# Node 1 returns: {\"messages\": [\"A\"]}\n# Node 2 returns: {\"messages\": [\"B\"]}\n# Final: {\"messages\": [\"B\"]}  # \"A\" is LOST!\n\n# CORRECT: Use Annotated with operator.add\nfrom typing import Annotated\nimport operator\n\nclass State(TypedDict):\n    messages: Annotated[list, operator.add]\n# Final: {\"messages\": [\"A\", \"B\"]}\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nWithout ReducedValue, arrays are overwritten not appended.\n```typescript\n\u002F\u002F WRONG: Array will be overwritten\nconst State = new StateSchema({\n  items: z.array(z.string()),  \u002F\u002F No reducer!\n});\n\u002F\u002F Node 1: { items: [\"A\"] }, Node 2: { items: [\"B\"] }\n\u002F\u002F Final: { items: [\"B\"] }  \u002F\u002F A is lost!\n\n\u002F\u002F CORRECT: Use ReducedValue\nconst State = new StateSchema({\n  items: new ReducedValue(\n    z.array(z.string()).default(() => []),\n    { reducer: (current, update) => current.concat(update) }\n  ),\n});\n\u002F\u002F Final: { items: [\"A\", \"B\"] }\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-forgot-reducer-for-list>\n\n\u003Cfix-state-must-return-dict>\n\u003Cpython>\nNodes must return partial updates, not mutate and return full state.\n```python\n# WRONG: Returning entire state object\ndef my_node(state: State) -> State:\n    state[\"field\"] = \"updated\"\n    return state  # Don't mutate and return!\n\n# CORRECT: Return dict with only the updates\ndef my_node(state: State) -> dict:\n    return {\"field\": \"updated\"}\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nReturn partial updates only, not the full state object.\n```typescript\n\u002F\u002F WRONG: Returning entire state\nconst myNode = async (state: typeof State.State) => {\n  state.field = \"updated\";\n  return state;  \u002F\u002F Don't do this!\n};\n\n\u002F\u002F CORRECT: Return partial updates\nconst myNode = async (state: typeof State.State) => {\n  return { field: \"updated\" };\n};\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-state-must-return-dict>\n\n---\n\n## Nodes\n\n\u003Cnode-function-signatures>\n\nNode functions accept these arguments:\n\n\u003Cpython>\n\n| Signature | When to Use |\n|-----------|-------------|\n| `def node(state: State)` | Simple nodes that only need state |\n| `def node(state: State, config: RunnableConfig)` | Need thread_id, tags, or configurable values |\n| `def node(state: State, runtime: Runtime[Context])` | Need runtime context, store, or stream_writer |\n\n```python\nfrom langchain_core.runnables import RunnableConfig\nfrom langgraph.runtime import Runtime\n\ndef plain_node(state: State):\n    return {\"results\": \"done\"}\n\ndef node_with_config(state: State, config: RunnableConfig):\n    thread_id = config[\"configurable\"][\"thread_id\"]\n    return {\"results\": f\"Thread: {thread_id}\"}\n\ndef node_with_runtime(state: State, runtime: Runtime[Context]):\n    user_id = runtime.context.user_id\n    return {\"results\": f\"User: {user_id}\"}\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\n\n| Signature | When to Use |\n|-----------|-------------|\n| `(state) => {...}` | Simple nodes that only need state |\n| `(state, config) => {...}` | Need thread_id, tags, or configurable values |\n\n```typescript\nimport { GraphNode, StateSchema } from \"@langchain\u002Flanggraph\";\n\nconst plainNode: GraphNode\u003Ctypeof State> = (state) => {\n  return { results: \"done\" };\n};\n\nconst nodeWithConfig: GraphNode\u003Ctypeof State> = (state, config) => {\n  const threadId = config?.configurable?.thread_id;\n  return { results: `Thread: ${threadId}` };\n};\n```\n\u003C\u002Ftypescript>\n\n\u003C\u002Fnode-function-signatures>\n\n---\n\n## Edges\n\n\u003Cedge-type-selection>\n\n| Need | Edge Type | When to Use |\n|------|-----------|-------------|\n| Always go to same node | `add_edge()` | Fixed, deterministic flow |\n| Route based on state | `add_conditional_edges()` | Dynamic branching |\n| Update state AND route | `Command` | Combine logic in single node |\n| Fan-out to multiple nodes | `Send` | Parallel processing with dynamic inputs |\n\n\u003C\u002Fedge-type-selection>\n\n\u003Cex-basic-graph>\n\u003Cpython>\nSimple two-node graph with linear edges.\n```python\nfrom langgraph.graph import StateGraph, START, END\nfrom typing_extensions import TypedDict\n\nclass State(TypedDict):\n    input: str\n    output: str\n\ndef process_input(state: State) -> dict:\n    return {\"output\": f\"Processed: {state['input']}\"}\n\ndef finalize(state: State) -> dict:\n    return {\"output\": state[\"output\"].upper()}\n\ngraph = (\n    StateGraph(State)\n    .add_node(\"process\", process_input)\n    .add_node(\"finalize\", finalize)\n    .add_edge(START, \"process\")\n    .add_edge(\"process\", \"finalize\")\n    .add_edge(\"finalize\", END)\n    .compile()\n)\n\nresult = graph.invoke({\"input\": \"hello\"})\nprint(result[\"output\"])  # \"PROCESSED: HELLO\"\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nChain nodes with addEdge and compile before invoking.\n```typescript\nimport { StateGraph, StateSchema, START, END } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  input: z.string(),\n  output: z.string().default(\"\"),\n});\n\nconst processInput = async (state: typeof State.State) => {\n  return { output: `Processed: ${state.input}` };\n};\n\nconst finalize = async (state: typeof State.State) => {\n  return { output: state.output.toUpperCase() };\n};\n\nconst graph = new StateGraph(State)\n  .addNode(\"process\", processInput)\n  .addNode(\"finalize\", finalize)\n  .addEdge(START, \"process\")\n  .addEdge(\"process\", \"finalize\")\n  .addEdge(\"finalize\", END)\n  .compile();\n\nconst result = await graph.invoke({ input: \"hello\" });\nconsole.log(result.output);  \u002F\u002F \"PROCESSED: HELLO\"\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-basic-graph>\n\n\u003Cex-conditional-edges>\n\u003Cpython>\nRoute to different nodes based on state with conditional edges.\n```python\nfrom typing import Literal\nfrom langgraph.graph import StateGraph, START, END\n\nclass State(TypedDict):\n    query: str\n    route: str\n    result: str\n\ndef classify(state: State) -> dict:\n    if \"weather\" in state[\"query\"].lower():\n        return {\"route\": \"weather\"}\n    return {\"route\": \"general\"}\n\ndef route_query(state: State) -> Literal[\"weather\", \"general\"]:\n    return state[\"route\"]\n\ngraph = (\n    StateGraph(State)\n    .add_node(\"classify\", classify)\n    .add_node(\"weather\", lambda s: {\"result\": \"Sunny, 72F\"})\n    .add_node(\"general\", lambda s: {\"result\": \"General response\"})\n    .add_edge(START, \"classify\")\n    .add_conditional_edges(\"classify\", route_query, [\"weather\", \"general\"])\n    .add_edge(\"weather\", END)\n    .add_edge(\"general\", END)\n    .compile()\n)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\naddConditionalEdges routes based on function return value.\n```typescript\nimport { StateGraph, StateSchema, START, END } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  query: z.string(),\n  route: z.string().default(\"\"),\n  result: z.string().default(\"\"),\n});\n\nconst classify = async (state: typeof State.State) => {\n  if (state.query.toLowerCase().includes(\"weather\")) {\n    return { route: \"weather\" };\n  }\n  return { route: \"general\" };\n};\n\nconst routeQuery = (state: typeof State.State) => state.route;\n\nconst graph = new StateGraph(State)\n  .addNode(\"classify\", classify)\n  .addNode(\"weather\", async () => ({ result: \"Sunny, 72F\" }))\n  .addNode(\"general\", async () => ({ result: \"General response\" }))\n  .addEdge(START, \"classify\")\n  .addConditionalEdges(\"classify\", routeQuery, [\"weather\", \"general\"])\n  .addEdge(\"weather\", END)\n  .addEdge(\"general\", END)\n  .compile();\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-conditional-edges>\n\n---\n\n## Command\n\nCommand combines state updates and routing in a single return value. Fields:\n- **`update`**: State updates to apply (like returning a dict from a node)\n- **`goto`**: Node name(s) to navigate to next\n- **`resume`**: Value to resume after `interrupt()` — see human-in-the-loop skill\n\n\u003Cex-command-state-and-routing>\n\u003Cpython>\nCommand lets you update state AND choose next node in one return.\n```python\nfrom langgraph.types import Command\nfrom typing import Literal\n\nclass State(TypedDict):\n    count: int\n    result: str\n\ndef node_a(state: State) -> Command[Literal[\"node_b\", \"node_c\"]]:\n    \"\"\"Update state AND decide next node in one return.\"\"\"\n    new_count = state[\"count\"] + 1\n    if new_count > 5:\n        return Command(update={\"count\": new_count}, goto=\"node_c\")\n    return Command(update={\"count\": new_count}, goto=\"node_b\")\n\ngraph = (\n    StateGraph(State)\n    .add_node(\"node_a\", node_a)\n    .add_node(\"node_b\", lambda s: {\"result\": \"B\"})\n    .add_node(\"node_c\", lambda s: {\"result\": \"C\"})\n    .add_edge(START, \"node_a\")\n    .add_edge(\"node_b\", END)\n    .add_edge(\"node_c\", END)\n    .compile()\n)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nReturn Command with update and goto to combine state change with routing.\n```typescript\nimport { StateGraph, StateSchema, START, END, Command } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  count: z.number().default(0),\n  result: z.string().default(\"\"),\n});\n\nconst nodeA = async (state: typeof State.State) => {\n  const newCount = state.count + 1;\n  if (newCount > 5) {\n    return new Command({ update: { count: newCount }, goto: \"node_c\" });\n  }\n  return new Command({ update: { count: newCount }, goto: \"node_b\" });\n};\n\nconst graph = new StateGraph(State)\n  .addNode(\"node_a\", nodeA, { ends: [\"node_b\", \"node_c\"] })\n  .addNode(\"node_b\", async () => ({ result: \"B\" }))\n  .addNode(\"node_c\", async () => ({ result: \"C\" }))\n  .addEdge(START, \"node_a\")\n  .addEdge(\"node_b\", END)\n  .addEdge(\"node_c\", END)\n  .compile();\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-command-state-and-routing>\n\n\u003Ccommand-return-type-annotations>\n\n**Python**: Use `Command[Literal[\"node_a\", \"node_b\"]]` as the return type annotation to declare valid goto destinations.\n\n**TypeScript**: Pass `{ ends: [\"node_a\", \"node_b\"] }` as the third argument to `addNode` to declare valid goto destinations.\n\n\u003C\u002Fcommand-return-type-annotations>\n\n\u003Cwarning-command-static-edges>\n\n**Warning**: `Command` only adds **dynamic** edges — static edges defined with `add_edge` \u002F `addEdge` still execute. If `node_a` returns `Command(goto=\"node_c\")` and you also have `graph.add_edge(\"node_a\", \"node_b\")`, **both** `node_b` and `node_c` will run.\n\n\u003C\u002Fwarning-command-static-edges>\n\n---\n\n## Send API\n\nFan-out with `Send`: return `[Send(\"worker\", {...})]` from a conditional edge to spawn parallel workers. Requires a reducer on the results field.\n\n\u003Cex-orchestrator-worker>\n\u003Cpython>\nFan out tasks to parallel workers using the Send API and aggregate results.\n```python\nfrom langgraph.types import Send\nfrom typing import Annotated\nimport operator\n\nclass OrchestratorState(TypedDict):\n    tasks: list[str]\n    results: Annotated[list, operator.add]\n    summary: str\n\ndef orchestrator(state: OrchestratorState):\n    \"\"\"Fan out tasks to workers.\"\"\"\n    return [Send(\"worker\", {\"task\": task}) for task in state[\"tasks\"]]\n\ndef worker(state: dict) -> dict:\n    return {\"results\": [f\"Completed: {state['task']}\"]}\n\ndef synthesize(state: OrchestratorState) -> dict:\n    return {\"summary\": f\"Processed {len(state['results'])} tasks\"}\n\ngraph = (\n    StateGraph(OrchestratorState)\n    .add_node(\"worker\", worker)\n    .add_node(\"synthesize\", synthesize)\n    .add_conditional_edges(START, orchestrator, [\"worker\"])\n    .add_edge(\"worker\", \"synthesize\")\n    .add_edge(\"synthesize\", END)\n    .compile()\n)\n\nresult = graph.invoke({\"tasks\": [\"Task A\", \"Task B\", \"Task C\"]})\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nFan out tasks to parallel workers using the Send API and aggregate results.\n```typescript\nimport { Send, StateGraph, StateSchema, ReducedValue, START, END } from \"@langchain\u002Flanggraph\";\nimport { z } from \"zod\";\n\nconst State = new StateSchema({\n  tasks: z.array(z.string()),\n  results: new ReducedValue(\n    z.array(z.string()).default(() => []),\n    { reducer: (curr, upd) => curr.concat(upd) }\n  ),\n  summary: z.string().default(\"\"),\n});\n\nconst orchestrator = (state: typeof State.State) => {\n  return state.tasks.map((task) => new Send(\"worker\", { task }));\n};\n\nconst worker = async (state: { task: string }) => {\n  return { results: [`Completed: ${state.task}`] };\n};\n\nconst synthesize = async (state: typeof State.State) => {\n  return { summary: `Processed ${state.results.length} tasks` };\n};\n\nconst graph = new StateGraph(State)\n  .addNode(\"worker\", worker)\n  .addNode(\"synthesize\", synthesize)\n  .addConditionalEdges(START, orchestrator, [\"worker\"])\n  .addEdge(\"worker\", \"synthesize\")\n  .addEdge(\"synthesize\", END)\n  .compile();\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-orchestrator-worker>\n\n\u003Cfix-send-accumulator>\n\u003Cpython>\nUse a reducer to accumulate parallel worker results (otherwise last worker overwrites).\n```python\n# WRONG: No reducer - last worker overwrites\nclass State(TypedDict):\n    results: list\n\n# CORRECT\nclass State(TypedDict):\n    results: Annotated[list, operator.add]  # Accumulates\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse ReducedValue to accumulate parallel worker results.\n```typescript\n\u002F\u002F WRONG: No reducer\nconst State = new StateSchema({ results: z.array(z.string()) });\n\n\u002F\u002F CORRECT\nconst State = new StateSchema({\n  results: new ReducedValue(z.array(z.string()).default(() => []), { reducer: (curr, upd) => curr.concat(upd) }),\n});\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-send-accumulator>\n\n---\n\n## Running Graphs: Invoke and Stream\n\n\u003Cinvoke-basics>\n\nCall `graph.invoke(input, config)` to run a graph to completion and return the final state.\n\n\u003Cpython>\n```python\nresult = graph.invoke({\"input\": \"hello\"})\n# With config (for persistence, tags, etc.)\nresult = graph.invoke({\"input\": \"hello\"}, {\"configurable\": {\"thread_id\": \"1\"}})\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\n```typescript\nconst result = await graph.invoke({ input: \"hello\" });\n\u002F\u002F With config\nconst result = await graph.invoke({ input: \"hello\" }, { configurable: { thread_id: \"1\" } });\n```\n\u003C\u002Ftypescript>\n\n\u003C\u002Finvoke-basics>\n\n\u003Cstream-mode-selection>\n\n| Mode | What it Streams | Use Case |\n|------|----------------|----------|\n| `values` | Full state after each step | Monitor complete state |\n| `updates` | State deltas | Track incremental updates |\n| `messages` | LLM tokens + metadata | Chat UIs |\n| `custom` | User-defined data | Progress indicators |\n\n\u003C\u002Fstream-mode-selection>\n\n\u003Cex-stream-llm-tokens>\n\u003Cpython>\nStream LLM tokens in real-time for chat UI display.\n```python\nfor chunk in graph.stream(\n    {\"messages\": [HumanMessage(\"Hello\")]},\n    stream_mode=\"messages\"\n):\n    token, metadata = chunk\n    if hasattr(token, \"content\"):\n        print(token.content, end=\"\", flush=True)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nStream LLM tokens in real-time for chat UI display.\n```typescript\nfor await (const chunk of graph.stream(\n  { messages: [new HumanMessage(\"Hello\")] },\n  { streamMode: \"messages\" }\n)) {\n  const [token, metadata] = chunk;\n  if (token.content) {\n    process.stdout.write(token.content);\n  }\n}\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-stream-llm-tokens>\n\n\u003Cex-stream-custom-data>\n\u003Cpython>\nEmit custom progress updates from within nodes using the stream writer.\n```python\nfrom langgraph.config import get_stream_writer\n\ndef my_node(state):\n    writer = get_stream_writer()\n    writer(\"Processing step 1...\")\n    # Do work\n    writer(\"Complete!\")\n    return {\"result\": \"done\"}\n\nfor chunk in graph.stream({\"data\": \"test\"}, stream_mode=\"custom\"):\n    print(chunk)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nEmit custom progress updates from within nodes using the stream writer.\n```typescript\nimport { getWriter } from \"@langchain\u002Flanggraph\";\n\nconst myNode = async (state: typeof State.State) => {\n  const writer = getWriter();\n  writer(\"Processing step 1...\");\n  \u002F\u002F Do work\n  writer(\"Complete!\");\n  return { result: \"done\" };\n};\n\nfor await (const chunk of graph.stream({ data: \"test\" }, { streamMode: \"custom\" })) {\n  console.log(chunk);\n}\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-stream-custom-data>\n\n---\n\n## Error Handling\n\nMatch the error type to the right handler:\n\n\u003Cerror-handling-table>\n\n| Error Type | Who Fixes | Strategy | Example |\n|---|---|---|---|\n| Transient (network, rate limits) | System | `RetryPolicy(max_attempts=3)` | `add_node(..., retry_policy=...)` |\n| LLM-recoverable (tool failures) | LLM | `ToolNode(tools, handle_tool_errors=True)` | Error returned as ToolMessage |\n| User-fixable (missing info) | Human | `interrupt({\"message\": ...})` | Collect missing data (see HITL skill) |\n| Unexpected | Developer | Let bubble up | `raise` |\n\n\u003C\u002Ferror-handling-table>\n\n\u003Cex-retry-policy>\n\u003Cpython>\nUse RetryPolicy for transient errors (network issues, rate limits).\n```python\nfrom langgraph.types import RetryPolicy\n\nworkflow.add_node(\n    \"search_documentation\",\n    search_documentation,\n    retry_policy=RetryPolicy(max_attempts=3, initial_interval=1.0)\n)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse retryPolicy for transient errors.\n```typescript\nworkflow.addNode(\n  \"searchDocumentation\",\n  searchDocumentation,\n  {\n    retryPolicy: { maxAttempts: 3, initialInterval: 1.0 },\n  },\n);\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-retry-policy>\n\n\u003Cex-tool-node-error-handling>\n\u003Cpython>\nUse ToolNode from langgraph.prebuilt to handle tool execution and errors. When handle_tool_errors=True, errors are returned as ToolMessages so the LLM can recover.\n```python\nfrom langgraph.prebuilt import ToolNode\n\ntool_node = ToolNode(tools, handle_tool_errors=True)\n\nworkflow.add_node(\"tools\", tool_node)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse ToolNode from @langchain\u002Flanggraph\u002Fprebuilt to handle tool execution and errors. When handleToolErrors is true, errors are returned as ToolMessages so the LLM can recover.\n```typescript\nimport { ToolNode } from \"@langchain\u002Flanggraph\u002Fprebuilt\";\n\nconst toolNode = new ToolNode(tools, { handleToolErrors: true });\n\nworkflow.addNode(\"tools\", toolNode);\n```\n\u003C\u002Ftypescript>\n\u003C\u002Fex-tool-node-error-handling>\n\n---\n\n## Common Fixes\n\n\u003Cfix-compile-before-execution>\n\u003Cpython>\nMust compile() to get executable graph.\n```python\n# WRONG\nbuilder.invoke({\"input\": \"test\"})  # AttributeError!\n\n# CORRECT\ngraph = builder.compile()\ngraph.invoke({\"input\": \"test\"})\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nMust compile() to get executable graph.\n```typescript\n\u002F\u002F WRONG\nawait builder.invoke({ input: \"test\" });\n\n\u002F\u002F CORRECT\nconst graph = builder.compile();\nawait graph.invoke({ input: \"test\" });\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-compile-before-execution>\n\n\u003Cfix-infinite-loop-needs-exit>\n\u003Cpython>\nProvide conditional path to END to avoid infinite loops.\n```python\n# WRONG: Loops forever\nbuilder.add_edge(\"node_a\", \"node_b\")\nbuilder.add_edge(\"node_b\", \"node_a\")\n\n# CORRECT\ndef should_continue(state):\n    return END if state[\"count\"] > 10 else \"node_b\"\nbuilder.add_conditional_edges(\"node_a\", should_continue)\n```\n\u003C\u002Fpython>\n\u003Ctypescript>\nUse conditional edges with END return to break loops.\n```typescript\n\u002F\u002F WRONG: Loops forever\nbuilder.addEdge(\"node_a\", \"node_b\").addEdge(\"node_b\", \"node_a\");\n\n\u002F\u002F CORRECT\nbuilder.addConditionalEdges(\"node_a\", (state) => state.count > 10 ? END : \"node_b\");\n```\n\u003C\u002Ftypescript>\n\u003C\u002Ffix-infinite-loop-needs-exit>\n\n\u003Cfix-common-mistakes>\nOther common mistakes:\n```python\n# Router must return names of nodes that exist in the graph\nbuilder.add_node(\"my_node\", func)  # Add node BEFORE referencing in edges\nbuilder.add_conditional_edges(\"node_a\", router, [\"my_node\"])\n\n# Command return type needs Literal for routing destinations (Python)\ndef node_a(state) -> Command[Literal[\"node_b\", \"node_c\"]]:\n    return Command(goto=\"node_b\")\n\n# START is entry-only - cannot route back to it\nbuilder.add_edge(\"node_a\", START)  # WRONG!\nbuilder.add_edge(\"node_a\", \"entry\")  # Use a named entry node instead\n\n# Reducer expects matching types\nreturn {\"items\": [\"item\"]}  # List for list reducer, not a string\n```\n```typescript\n\u002F\u002F Always await graph.invoke() - it returns a Promise\nconst result = await graph.invoke({ input: \"test\" });\n\n\u002F\u002F TS Command nodes need { ends } to declare routing destinations\nbuilder.addNode(\"router\", routerFn, { ends: [\"node_b\", \"node_c\"] });\n```\n\u003C\u002Ffix-common-mistakes>\n\n\u003Cboundaries>\n### What You Should NOT Do\n\n- Mutate state directly — always return partial update dicts from nodes\n- Route back to START — it's entry-only; use a named node instead\n- Forget reducers on list fields — without one, last write wins\n- Mix static edges with Command goto without understanding both will execute\n\u003C\u002Fboundaries>\n",{"name":5,"description":7},"main","1ca81b0a7de199942400f965edd4d7544077ac5d",".continue\u002Fskills\u002Flanggraph-fundamentals","https:\u002F\u002Fgithub.com\u002FCoralShades\u002Facm-ai","https:\u002F\u002Fgithub.com\u002FCoralShades","coralshades.com.au","2026-04-18 00:45:07",{"source":8,"skillName":5,"commands":40,"prompt":50,"zipAvailable":51},[41,44,47],{"tool":42,"command":43},"npx","npx skills add CoralShades\u002Facm-ai --skill langgraph-fundamentals",{"tool":45,"command":46},"bunx","bunx skills add CoralShades\u002Facm-ai --skill langgraph-fundamentals",{"tool":48,"command":49},"pnpm","pnpm dlx skills add CoralShades\u002Facm-ai --skill langgraph-fundamentals","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FCoralShades\u002Facm-ai\u002Fblob\u002Fmain\u002F.continue\u002Fskills\u002Flanggraph-fundamentals\u002FSKILL.md",true,[],[54,77,90,104,124,129],{"skillNo":55,"name":56,"nameZh":6,"description":57,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":58,"categories":59,"keywords":65,"publishedAt":27},"01M6C63HKXA890HHV47WBAX0TB","uncodixfy","Prevents generic AI\u002FCodex UI patterns when generating frontend code. Use this skill whenever generating HTML, CSS, React, Vue, Svelte, or any frontend UI code to enforce clean, human-designed aesthetics inspired by Linear, Raycast, Stripe, and GitHub instead of typical AI-generated UI.","https:\u002F\u002Fgithub.com\u002FCoralShades\u002Facm-ai\u002Fblob\u002Fmain\u002F.qwen\u002Fskills\u002Funcodixfy\u002FSKILL.md",[60],{"categoryNo":61,"categoryName":62,"taxonomyType":63,"taxonomyTypeDesc":64},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[56,66,67,68,69,70,22,71,72,73,74,75,76],"github","frontend","css","codex","react","linear","ui","vue","generated","use","html",{"skillNo":78,"name":79,"nameZh":6,"description":80,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":81,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":84,"publishedAt":27},"01M6C63FJ2GEBBGZ5XSMV40KXB","runpodctl","Runpod CLI to manage your GPU workloads.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FCoralShades\u002Facm-ai\u002Fblob\u002Fmain\u002F.qwen\u002Fskills\u002Frunpodctl\u002FSKILL.md",[],[79,85,86,87,88,89],"macos","cli","linux","gpu","Bash",{"skillNo":91,"name":92,"nameZh":6,"description":93,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":94,"categories":95,"keywords":96,"publishedAt":27},"01M6C63DPAV46TFVQ01SFYPJ7X","langgraph-persistence","INVOKE THIS SKILL when your LangGraph needs to persist state, remember conversations, travel through history, or configure subgraph checkpointer scoping. Covers checkpointers, thread_id, time travel, Store, and subgraph persistence modes.","https:\u002F\u002Fgithub.com\u002FCoralShades\u002Facm-ai\u002Fblob\u002Fmain\u002F.qwen\u002Fskills\u002Flanggraph-persistence\u002FSKILL.md",[],[97,19,98,99,100,101,102,24,103],"langgraph persistence","persistence","travel","history","scoping","remember","configure",{"skillNo":105,"name":106,"nameZh":6,"description":107,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":115,"publishedAt":27},"01M6C63BNS9CQEBTB8RNSSBFPA","langgraph-human-in-the-loop","INVOKE THIS SKILL when implementing human-in-the-loop patterns, pausing for approval, or handling errors in LangGraph. Covers interrupt(), Command(resume=...), approval\u002Fvalidation workflows, and the 4-tier error handling strategy.","https:\u002F\u002Fgithub.com\u002FCoralShades\u002Facm-ai\u002Fblob\u002Fmain\u002F.qwen\u002Fskills\u002Flanggraph-human-in-the-loop\u002FSKILL.md",[110],{"categoryNo":111,"categoryName":112,"taxonomyType":113,"taxonomyTypeDesc":114},"01JBSEEDCAT000000000000038","人力资源","PROFESSION","适用职业",[116,117,19,118,23,119,120,26,121,122,123],"langgraph human in the loop","loop","workflows","validation","human-in-the-loop","strategy","approval","resume",{"skillNo":125,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":128,"publishedAt":27},"01M6C639GVYZFX3NFPV5FBDR4F","https:\u002F\u002Fgithub.com\u002FCoralShades\u002Facm-ai\u002Fblob\u002Fmain\u002F.qwen\u002Fskills\u002Flanggraph-fundamentals\u002FSKILL.md",[],[20,19,23,24,25,21,26,22],{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":135,"publishedAt":27},"01M6C6375N34YX2GZ3SD3R7XR2","langchain-rag","INVOKE THIS SKILL when building ANY retrieval-augmented generation (RAG) system. Covers document loaders, RecursiveCharacterTextSplitter, embeddings (OpenAI), and vector stores (Chroma, FAISS, Pinecone).","https:\u002F\u002Fgithub.com\u002FCoralShades\u002Facm-ai\u002Fblob\u002Fmain\u002F.qwen\u002Fskills\u002Flangchain-rag\u002FSKILL.md",[],[136,137,138,139,140,141,142,143,144,145],"rag","langchain rag","langchain","embeddings","vector","openai","chroma","faiss","vector stores","system"]