Problems with subsequent requests - First incoming request is coming, but second request is not coming #305

Closed
opened 2026-02-15 18:15:41 -05:00 by yindo · 1 comment
Owner

Originally created by @saireddy-tk on GitHub (Jul 14, 2025).

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangGraph.js documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangGraph.js rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package).

Example Code

import { StateGraph, END, START, Annotation } from '@langchain/langgraph'
import { getLogger } from '../app-logger/app-logger.mjs'

const logger = getLogger()

export class GraphBuilder {
  constructor(config, nodes, initialState, checkpointer) {
    this.config = config
    this.nodes = nodes
    this.edges = config.edges || []
    this.initialState = initialState
    this.checkpointer = checkpointer
    this.stateAnnotation = this.createStateAnnotation()
  }

  createStateAnnotation() {
    const baseFields = {}
    const discoveredFields = {}

    const customReducer = (currentState, updateValue) => {
      // Handle undefined updates
      if (updateValue === undefined) return currentState

      // Array concatenation
      if (Array.isArray(currentState) && Array.isArray(updateValue)) {
        return currentState.concat(updateValue)
      }

      // Default: replace with new value
      return updateValue
    }

    // Process initial state
    if (this.initialState) {
      for (const [key, value] of Object.entries(this.initialState)) {
        baseFields[key] = Annotation({
          reducer: customReducer,
          default: () => {
            if (Array.isArray(value)) return value || []
            if (typeof value === 'object' && value !== null) return value
            return value || ''
          },
        })
      }
    }

    // Process node configurations
    for (const node of this.config.nodes) {
      // Handle input validations
      if (node.input?.validations) {
        for (const [key, validation] of Object.entries(node.input.validations)) {
          if (!baseFields[key] && !discoveredFields[key]) {
            discoveredFields[key] = Annotation({
              reducer: customReducer,
              default: () => {
                if (validation.type === 'array') return []
                if (validation.type === 'object') return {}
                return ''
              },
            })
          }
        }
      }

      // Handle input schema
      if (node.input?.custom) {
        for (const [key, value] of Object.entries(node.input.custom)) {
          if (!baseFields[key] && !discoveredFields[key]) {
            discoveredFields[key] = Annotation({
              reducer: customReducer,
              default: () => {
                if (Array.isArray(value)) return value || []
                if (typeof value === 'object' && value !== null) return value
                return value || ''
              },
            })
          }
        }
      }

      // Handle output schema
      if (node.output?.schema?.properties) {
        for (const [key, prop] of Object.entries(node.output.schema.properties)) {
          if (!baseFields[key] && !discoveredFields[key]) {
            discoveredFields[key] = Annotation({
              reducer: customReducer,
              default: () => {
                if (prop.type === 'array') return []
                if (prop.type === 'object') return {}
                return ''
              },
            })
          }
        }
      }

      // Handle output key
      if (node.output?.key && !baseFields[node.output.key] && !discoveredFields[node.output.key]) {
        discoveredFields[node.output.key] = Annotation({
          reducer: customReducer,
          default: () => '',
        })
      }
    }

    return Annotation.Root({ ...baseFields, ...discoveredFields })
  }

  addNode(workflow, nodes) {
    for (const node of nodes) {
      workflow.addNode(node.id, async (state) => {
        const nodeInstance = await this.nodes.get(node.id)
        return await nodeInstance.execute(state)
      })
    }
  }

  addEdge(workflow, nodes) {
    // Find all nodes that should start in parallel
    const startNodes = this.config.nodes.filter((node) => !this.edges.some((edge) => edge.destination === node.id))

    // Add edges from START to all starting nodes (parallel execution)
    for (const startNode of startNodes) {
      workflow.addEdge(START, startNode.id)
    }

    // Add all other edges as defined
    for (const node of nodes) {
      const nodeId = node.id
      const outgoingEdges = this.edges.filter((edge) => edge.source === nodeId)

      // Condition nodes use conditional edges with array-based targets
      if (node.type === 'condition') workflow.addConditionalEdges(nodeId, this.routeCondition)
      else if (outgoingEdges.length === 0) workflow.addEdge(nodeId, END)
      else outgoingEdges.forEach((edge) => workflow.addEdge(nodeId, edge.destination === 'end' ? END : edge.destination))
    }
  }

  routeCondition(state) {
    const targets = state.nextTargets || []
    if (targets.length > 0) return targets.map((target) => (target === 'end' ? END : target))
    return END
  }

  build() {
    logger.info('Building LangGraph workflow...')
    // const workflow = new StateGraph({ channels: this.stateAnnotation })
    const workflow = new StateGraph(this.stateAnnotation)
    this.addNode(workflow, this.config.nodes)
    this.addEdge(workflow, this.config.nodes)

    return workflow.compile({ checkpointer: this.checkpointer })
  }
}

System Info

@langchain/core@0.3.62
@langchain/langgraph@0.3.6

Macbook pro

Originally created by @saireddy-tk on GitHub (Jul 14, 2025). ### Checked other resources - [x] I added a very descriptive title to this issue. - [x] I searched the LangGraph.js documentation with the integrated search. - [x] I used the GitHub search to find a similar question and didn't find it. - [x] I am sure that this is a bug in LangGraph.js rather than my code. - [x] The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package). ### Example Code ``` import { StateGraph, END, START, Annotation } from '@langchain/langgraph' import { getLogger } from '../app-logger/app-logger.mjs' const logger = getLogger() export class GraphBuilder { constructor(config, nodes, initialState, checkpointer) { this.config = config this.nodes = nodes this.edges = config.edges || [] this.initialState = initialState this.checkpointer = checkpointer this.stateAnnotation = this.createStateAnnotation() } createStateAnnotation() { const baseFields = {} const discoveredFields = {} const customReducer = (currentState, updateValue) => { // Handle undefined updates if (updateValue === undefined) return currentState // Array concatenation if (Array.isArray(currentState) && Array.isArray(updateValue)) { return currentState.concat(updateValue) } // Default: replace with new value return updateValue } // Process initial state if (this.initialState) { for (const [key, value] of Object.entries(this.initialState)) { baseFields[key] = Annotation({ reducer: customReducer, default: () => { if (Array.isArray(value)) return value || [] if (typeof value === 'object' && value !== null) return value return value || '' }, }) } } // Process node configurations for (const node of this.config.nodes) { // Handle input validations if (node.input?.validations) { for (const [key, validation] of Object.entries(node.input.validations)) { if (!baseFields[key] && !discoveredFields[key]) { discoveredFields[key] = Annotation({ reducer: customReducer, default: () => { if (validation.type === 'array') return [] if (validation.type === 'object') return {} return '' }, }) } } } // Handle input schema if (node.input?.custom) { for (const [key, value] of Object.entries(node.input.custom)) { if (!baseFields[key] && !discoveredFields[key]) { discoveredFields[key] = Annotation({ reducer: customReducer, default: () => { if (Array.isArray(value)) return value || [] if (typeof value === 'object' && value !== null) return value return value || '' }, }) } } } // Handle output schema if (node.output?.schema?.properties) { for (const [key, prop] of Object.entries(node.output.schema.properties)) { if (!baseFields[key] && !discoveredFields[key]) { discoveredFields[key] = Annotation({ reducer: customReducer, default: () => { if (prop.type === 'array') return [] if (prop.type === 'object') return {} return '' }, }) } } } // Handle output key if (node.output?.key && !baseFields[node.output.key] && !discoveredFields[node.output.key]) { discoveredFields[node.output.key] = Annotation({ reducer: customReducer, default: () => '', }) } } return Annotation.Root({ ...baseFields, ...discoveredFields }) } addNode(workflow, nodes) { for (const node of nodes) { workflow.addNode(node.id, async (state) => { const nodeInstance = await this.nodes.get(node.id) return await nodeInstance.execute(state) }) } } addEdge(workflow, nodes) { // Find all nodes that should start in parallel const startNodes = this.config.nodes.filter((node) => !this.edges.some((edge) => edge.destination === node.id)) // Add edges from START to all starting nodes (parallel execution) for (const startNode of startNodes) { workflow.addEdge(START, startNode.id) } // Add all other edges as defined for (const node of nodes) { const nodeId = node.id const outgoingEdges = this.edges.filter((edge) => edge.source === nodeId) // Condition nodes use conditional edges with array-based targets if (node.type === 'condition') workflow.addConditionalEdges(nodeId, this.routeCondition) else if (outgoingEdges.length === 0) workflow.addEdge(nodeId, END) else outgoingEdges.forEach((edge) => workflow.addEdge(nodeId, edge.destination === 'end' ? END : edge.destination)) } } routeCondition(state) { const targets = state.nextTargets || [] if (targets.length > 0) return targets.map((target) => (target === 'end' ? END : target)) return END } build() { logger.info('Building LangGraph workflow...') // const workflow = new StateGraph({ channels: this.stateAnnotation }) const workflow = new StateGraph(this.stateAnnotation) this.addNode(workflow, this.config.nodes) this.addEdge(workflow, this.config.nodes) return workflow.compile({ checkpointer: this.checkpointer }) } } ``` ### System Info @langchain/core@0.3.62 @langchain/langgraph@0.3.6 Macbook pro
yindo added the question label 2026-02-15 18:15:41 -05:00
yindo closed this issue 2026-02-15 18:15:41 -05:00
Author
Owner

@dqbd commented on GitHub (Jul 15, 2025):

Hello! The attached code snippet does not replicate the OOM issue. Comparing two code snippets, it does look like the former has a different reducer applied to more (?) channels compared to the latter non-Annotation snippet.

Is it possible to share a graph with repro steps?

If not would check

  1. Set the recursion_limit to a lower value
  2. Share a public LangSmith trace
@dqbd commented on GitHub (Jul 15, 2025): Hello! The attached code snippet does not replicate the OOM issue. Comparing two code snippets, it does look like the former has a different reducer applied to more (?) channels compared to the latter non-Annotation snippet. Is it possible to share a graph with repro steps? If not would check 1. Set the `recursion_limit` to a lower value 2. Share a public LangSmith trace
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#305