CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-langchain--langgraph

Low-level orchestration framework for building stateful, multi-actor applications with LLMs

Overview
Eval results
Files

types.mddocs/api/

Types & Errors

Core type definitions and error classes for LangGraph. Includes configuration types, runtime policies, and specialized error classes for graph execution failures.

Configuration Types

LangGraphRunnableConfig

Extended configuration type for LangGraph runnables with graph-specific options.

interface LangGraphRunnableConfig<
  Runtime = unknown
> extends RunnableConfig {
  configurable?: {
    thread_id?: string;
    checkpoint_id?: string;
    checkpoint_ns?: string;
    checkpoint_map?: Record<string, unknown>;
    [key: string]: unknown;
  };
  store?: BaseStore;
  writer?: <T>(chunk: T) => void;
  runtime?: Runtime;
}

RunnableConfig

Base configuration from LangChain Core.

interface RunnableConfig<ContextType = Record<string, any>> {
  tags?: string[];
  metadata?: Record<string, unknown>;
  callbacks?: Callbacks;
  runName?: string;
  maxConcurrency?: number;
  recursionLimit?: number;
  configurable?: Record<string, unknown>;
  runId?: string;
  signal?: AbortSignal;
}

Policy Types

RetryPolicy

Configuration for automatic retry of failed operations.

interface RetryPolicy {
  maxAttempts?: number;
  initialInterval?: number;
  backoffFactor?: number;
  maxInterval?: number;
  jitter?: boolean;
  retryOn?: (error: Error) => boolean;
}

Properties

  • maxAttempts - Maximum number of retry attempts (default: 3)
  • initialInterval - Initial retry delay in milliseconds (default: 500)
  • backoffFactor - Multiplier for delay between retries (default: 2)
  • maxInterval - Maximum retry delay in milliseconds (default: 60000)
  • jitter - Add random jitter to retry delays (default: true)
  • retryOn - Predicate to determine if error should trigger retry

Usage Examples

// Basic retry
const retryPolicy: RetryPolicy = {
  maxAttempts: 3,
  initialInterval: 1000,
  backoffFactor: 2
};

// With custom retry condition
const retryPolicy: RetryPolicy = {
  maxAttempts: 5,
  initialInterval: 500,
  retryOn: (error) => {
    return error.message.includes("timeout") ||
           error.message.includes("connection");
  }
};

// Exponential backoff with jitter
const retryPolicy: RetryPolicy = {
  maxAttempts: 4,
  initialInterval: 1000,
  backoffFactor: 2,
  maxInterval: 10000,
  jitter: true
};
// Delays: ~1s, ~2s, ~4s, ~8s (with jitter)

// Use with task
import { task } from "@langchain/langgraph";

const unreliableTask = task({
  name: "fetch",
  retry: retryPolicy
}, async (url: string) => {
  const response = await fetch(url);
  return response.json();
});

CachePolicy

Configuration for caching task results.

interface CachePolicy {
  keyFunc?: (input: unknown) => string;
  ttl?: number;
}

Properties

  • keyFunc - Function to generate cache key from input (default: JSON.stringify)
  • ttl - Time-to-live in milliseconds (optional)

Usage Examples

// Basic caching
const cachePolicy: CachePolicy = {
  keyFunc: (input) => JSON.stringify(input)
};

// With TTL
const cachePolicy: CachePolicy = {
  keyFunc: (input: any) => `${input.userId}-${input.query}`,
  ttl: 60000 // 1 minute
};

// Use with task
import { task } from "@langchain/langgraph";

const cachedTask = task({
  name: "expensive",
  cachePolicy: {
    keyFunc: (params: any) => `compute-${params.id}`,
    ttl: 300000 // 5 minutes
  }
}, async (params: any) => {
  return expensiveComputation(params);
});

// Use with node
graph.addNode("compute", computeNode, {
  cachePolicy: {
    keyFunc: (state) => state.userId
  }
});

Error Classes

BaseLangGraphError

Base error class for all LangGraph errors.

class BaseLangGraphError extends Error {
  lc_error_code?: string;

  constructor(message?: string, fields?: {
    lc_error_code?: string;
  });
}

GraphRecursionError

Thrown when graph exceeds maximum recursion limit.

class GraphRecursionError extends BaseLangGraphError {
  constructor(message?: string, fields?: {
    lc_error_code?: "GRAPH_RECURSION_LIMIT";
  });

  static readonly unminifiable_name: "GraphRecursionError";
}

Usage

try {
  await graph.invoke(input, {
    recursionLimit: 10
  });
} catch (error) {
  if (error instanceof GraphRecursionError) {
    console.error("Graph exceeded recursion limit");
  }
}

GraphValueError

Thrown for invalid graph values or configurations.

class GraphValueError extends BaseLangGraphError {
  constructor(message?: string, fields?: BaseLangGraphErrorFields);

  static readonly unminifiable_name: "GraphValueError";
}

GraphInterrupt

Thrown when graph execution is interrupted.

class GraphInterrupt extends BaseLangGraphError {
  interrupts: Interrupt[];

  constructor(
    interrupts?: Interrupt[],
    fields?: BaseLangGraphErrorFields
  );

  static readonly unminifiable_name: "GraphInterrupt";
}

function isGraphInterrupt(e?: unknown): e is GraphInterrupt;

NodeInterrupt

Raised by a node to interrupt execution for human-in-the-loop.

class NodeInterrupt extends GraphInterrupt {
  constructor(
    message: any,
    fields?: BaseLangGraphErrorFields
  );

  static readonly unminifiable_name: "NodeInterrupt";
}

Usage

import { NodeInterrupt, isGraphInterrupt } from "@langchain/langgraph";

const reviewNode = (state: any) => {
  if (state.needsReview) {
    throw new NodeInterrupt({
      question: "Approve this action?",
      data: state
    });
  }
  return { approved: true };
};

try {
  await graph.invoke(input);
} catch (error) {
  if (isGraphInterrupt(error)) {
    console.log("Execution interrupted");
    console.log(error.interrupts);
  }
}

EmptyInputError

Thrown when required input is missing.

class EmptyInputError extends BaseLangGraphError {
  constructor(message?: string, fields?: BaseLangGraphErrorFields);

  static readonly unminifiable_name: "EmptyInputError";
}

EmptyChannelError

Thrown when attempting to read from an empty channel.

class EmptyChannelError extends BaseLangGraphError {
  constructor(message?: string, fields?: BaseLangGraphErrorFields);

  static readonly unminifiable_name: "EmptyChannelError";
}

InvalidUpdateError

Thrown when a state update is invalid.

class InvalidUpdateError extends BaseLangGraphError {
  constructor(message?: string, fields?: {
    lc_error_code?: "INVALID_CONCURRENT_GRAPH_UPDATE" | "INVALID_GRAPH_NODE_RETURN_VALUE";
  });

  static readonly unminifiable_name: "InvalidUpdateError";
}

Common Causes

// Multiple nodes writing to LastValue channel
// Results in InvalidUpdateError with code INVALID_CONCURRENT_GRAPH_UPDATE

// Invalid node return value
// Results in InvalidUpdateError with code INVALID_GRAPH_NODE_RETURN_VALUE

UnreachableNodeError

Thrown when a node is defined but unreachable from the graph's entry point.

class UnreachableNodeError extends BaseLangGraphError {
  constructor(message?: string, fields?: {
    lc_error_code?: "UNREACHABLE_NODE";
  });

  static readonly unminifiable_name: "UnreachableNodeError";
}

Usage

try {
  const graph = new StateGraph(State)
    .addNode("node1", node1Fn)
    .addNode("node2", node2Fn) // Never connected
    .addEdge("__start__", "node1")
    .addEdge("node1", "__end__")
    .compile(); // Will throw UnreachableNodeError
} catch (error) {
  if (error instanceof UnreachableNodeError) {
    console.error("Node not reachable:", error.message);
  }
}

RemoteException

Thrown when an error occurs in a remote graph.

class RemoteException extends BaseLangGraphError {
  constructor(message?: string, fields?: BaseLangGraphErrorFields);

  static readonly unminifiable_name: "RemoteException";
}

Usage

import { RemoteGraph, RemoteException } from "@langchain/langgraph";

const remoteGraph = new RemoteGraph({
  graphId: "my-graph",
  url: "https://api.langgraph.cloud",
  apiKey: process.env.API_KEY
});

try {
  await remoteGraph.invoke(input);
} catch (error) {
  if (error instanceof RemoteException) {
    console.error("Remote graph error:", error.message);
  }
}

MultipleSubgraphsError

Deprecated error for multiple subgraphs (no longer thrown).

class MultipleSubgraphsError extends BaseLangGraphError {
  constructor(message?: string, fields?: {
    lc_error_code?: "MULTIPLE_SUBGRAPHS";
  });

  static readonly unminifiable_name: "MultipleSubgraphError";
}

GraphBubbleUp

Base class for errors that bubble up through the graph execution stack.

class GraphBubbleUp extends BaseLangGraphError {
  readonly is_bubble_up: true;
}

Usage

GraphBubbleUp is a base class used internally for errors that should propagate up the graph hierarchy. Both GraphInterrupt and ParentCommand extend this class.

import { isGraphBubbleUp } from "@langchain/langgraph";

try {
  await graph.invoke(input);
} catch (error) {
  if (isGraphBubbleUp(error)) {
    // This error should bubble up to parent graph
    console.log("Bubble up error:", error);
  }
}

ParentCommand

Exception used to send a command to a parent graph from a subgraph.

class ParentCommand extends GraphBubbleUp {
  command: Command;

  constructor(command: Command);

  static readonly unminifiable_name: "ParentCommand";
}

Usage

ParentCommand allows a subgraph to send commands to its parent graph during execution.

import { ParentCommand, Command, isParentCommand } from "@langchain/langgraph";

const subgraphNode = (state: any) => {
  if (state.needsParentAction) {
    // Send command to parent graph
    throw new ParentCommand(
      new Command({
        goto: "parent_node",
        update: { status: "requires_parent_action" }
      })
    );
  }
  return state;
};

// In parent graph
try {
  await subgraph.invoke(input);
} catch (error) {
  if (isParentCommand(error)) {
    const command = error.command;
    console.log("Received command from subgraph:", command);
    // Handle the command
  }
}

Error Type Guards

function isGraphBubbleUp(e?: unknown): e is GraphBubbleUp;
function isGraphInterrupt(e?: unknown): e is GraphInterrupt;
function isParentCommand(e?: unknown): e is ParentCommand;

Usage

import {
  isGraphBubbleUp,
  isGraphInterrupt,
  isParentCommand
} from "@langchain/langgraph";

try {
  await graph.invoke(input);
} catch (error) {
  if (isGraphInterrupt(error)) {
    console.log("Graph interrupted:", error.interrupts);
  } else if (isParentCommand(error)) {
    console.log("Parent command:", error.command);
  } else if (isGraphBubbleUp(error)) {
    console.log("Bubble up error");
  }
}

Error Handling Patterns

Comprehensive Error Handling

import {
  GraphRecursionError,
  GraphInterrupt,
  InvalidUpdateError,
  UnreachableNodeError,
  RemoteException
} from "@langchain/langgraph";

try {
  const result = await graph.invoke(input, config);
} catch (error) {
  if (error instanceof GraphRecursionError) {
    console.error("Recursion limit exceeded");
    // Increase recursion limit or check for infinite loops
  } else if (error instanceof GraphInterrupt) {
    console.log("Execution interrupted");
    console.log("Interrupts:", error.interrupts);
    // Handle human-in-the-loop
  } else if (error instanceof InvalidUpdateError) {
    console.error("Invalid state update:", error.message);
    // Check channel types and node return values
  } else if (error instanceof UnreachableNodeError) {
    console.error("Graph has unreachable nodes:", error.message);
    // Fix graph topology
  } else if (error instanceof RemoteException) {
    console.error("Remote graph error:", error.message);
    // Handle remote errors, possibly retry
  } else {
    console.error("Unexpected error:", error);
  }
}

Retry with Specific Errors

const retryPolicy: RetryPolicy = {
  maxAttempts: 3,
  initialInterval: 1000,
  backoffFactor: 2,
  retryOn: (error) => {
    // Retry on network errors but not on validation errors
    return !(error instanceof InvalidUpdateError) &&
           !(error instanceof GraphValueError);
  }
};

Interrupt Detection

import { isInterrupted, INTERRUPT } from "@langchain/langgraph";

const result = await graph.invoke(input, config);

if (isInterrupted(result)) {
  // Handle interrupt
  const interrupts = result[INTERRUPT];
  for (const interrupt of interrupts) {
    console.log("Interrupt ID:", interrupt.id);
    console.log("Interrupt value:", interrupt.value);
  }

  // Resume with user input
  const resumeResult = await graph.invoke(
    new Command({ resume: userInput }),
    config
  );
}

Type Utilities

Type Extraction

import type { StateType, UpdateType, NodeType } from "@langchain/langgraph";

const State = Annotation.Root({
  count: Annotation<number>,
  messages: Annotation<string[]>
});

type S = StateType<typeof State.spec>; // { count: number, messages: string[] }
type U = UpdateType<typeof State.spec>; // { count?: number, messages?: string[] }
type N = NodeType<typeof State.spec>; // RunnableLike<S, U | Partial<S>>

Runtime Type

import type { Runtime } from "@langchain/langgraph";

// Access runtime context in nodes
const node = async (state: State, config: LangGraphRunnableConfig<Runtime>) => {
  const runtime = config.runtime;
  // Access runtime properties
  return state;
};

Additional Type Utilities

type All = "*"; // Represents all nodes in interrupt configuration

type InferWriterType<T>; // Infer writer type from configuration
type InferInterruptInputType<T>; // Infer interrupt input type

interface DrawableGraph {
  nodes: DrawableNode[];
  edges: DrawableEdge[];
}

interface StreamEvent {
  // LangChain stream event structure
  event: string;
  data: unknown;
  name?: string;
  tags?: string[];
  metadata?: Record<string, unknown>;
}

Complete Example: Production Error Handling

import {
  StateGraph,
  Annotation,
  GraphRecursionError,
  GraphInterrupt,
  InvalidUpdateError,
  isInterrupted,
  INTERRUPT
} from "@langchain/langgraph";
import { MemorySaver } from "@langchain/langgraph-checkpoint";

const State = Annotation.Root({
  messages: Annotation<string[]>({
    reducer: (a, b) => a.concat(b),
    default: () => []
  }),
  retryCount: Annotation<number>
});

const graph = new StateGraph(State)
  .addNode("process", processNode, {
    retryPolicy: {
      maxAttempts: 3,
      initialInterval: 1000,
      retryOn: (error) => !error.message.includes("fatal")
    }
  })
  .compile({ checkpointer: new MemorySaver() });

async function robustInvoke(input: any, config: any) {
  try {
    const result = await graph.invoke(input, {
      ...config,
      recursionLimit: 25
    });

    if (isInterrupted(result)) {
      console.log("Waiting for user input");
      return {
        status: "interrupted",
        interrupts: result[INTERRUPT]
      };
    }

    return { status: "success", data: result };

  } catch (error) {
    if (error instanceof GraphRecursionError) {
      console.error("Recursion limit exceeded");
      return { status: "error", code: "RECURSION_LIMIT" };
    }

    if (error instanceof InvalidUpdateError) {
      console.error("Invalid update:", error.message);
      if (error.lc_error_code === "INVALID_CONCURRENT_GRAPH_UPDATE") {
        return { status: "error", code: "CONCURRENT_UPDATE" };
      }
    }

    if (error instanceof GraphInterrupt) {
      return {
        status: "interrupted",
        interrupts: error.interrupts
      };
    }

    // Log unexpected errors
    console.error("Unexpected error:", error);
    return { status: "error", code: "UNKNOWN", message: error.message };
  }
}

// Use it
const result = await robustInvoke(input, {
  configurable: { thread_id: "thread-1" }
});

Install with Tessl CLI

npx tessl i tessl/npm-langchain--langgraph@1.0.1

docs

api

channels.md

control-flow-api.md

execution-api.md

functional-api.md

graph-api.md

graph-construction-full.md

imports.md

persistence-api.md

persistence-full.md

prebuilt.md

remote.md

state-management.md

types.md

zod.md

index.md

tile.json