I have a question about handling user intervention in LangGraph.
In my use case, I sometimes need to pause execution before or after certain tools/nodes to get user approval, additional information, or a reason to proceed. I see that LangGraph provides the interrupt mechanism for this, but I’m wondering what the advantages are compared to simply calling Python’s built-in input() wherever I need it.
From my perspective:
Using input() feels straightforward and gives me the user’s response right away.
Using interrupt() seems more confusing to handle, especially when it comes to resuming the graph execution.
So my questions are:
What are the key advantages of using LangGraph’s interrupt instead of just input()?
Are there scenarios where interrupt is clearly better (e.g., for persistence, state management, async flows, etc.)?
I’d love to hear from anyone with experience on when and why interrupt is the better choice.
input() is really only appropriate for local, single-user terminal programs. In a web application it is a poor fit because it halts the thread indefinitely while waiting on stdin. During that pause, the server is unable to handle any other requests. Since browsers don’t communicate with stdin, the call will never complete anyway. Worse, if the process is stopped (because of a timeout, crash, application redeploy, etc) the application state is lost entirely. Each request would be pinned to a single process and could not be recovered or resumed elsewhere, which makes the design unscalable.
interrupt() gives you the same programming model when writing the agent code:: pause here, wait for input, then resume, but it is implemented in a way that works in distributed, production systems. When an interrupt is raised, execution is suspended and the graph’s state is checkpointed outside the process. The server remains free to continue handling other requests, and when the user’s response eventually arrives, it can be processed by any available worker. This makes the process itself stateless and fault-tolerant: if one machine goes down, another can seamlessly continue. It also enables horizontal scaling and load balancing, since no execution is tied to a specific server’s memory. It’s something you’d actually use in a web application, and a pattern you could scale indefinitely.
Thanks a lot for the clear and complete answer — the persistence and scalability points make perfect sense now.
One thing I’d like to clarify is how to implement automatic resuming in practice, especially when there are multiple different interrupts in the same graph. For example:
In one node I may ask the user “Do you approve it?”
In another place, I may ask “What’s your address?” and update state["address"]
Later, maybe I ask “What’s your budget?” and update state["budget"]
I understand the pattern for tool execution (adding ToolMessage and resuming), but in this case all of these points involve user intervention.
So my questions are:
How can I show the correct interrupt message to the user, depending on where the pause occurred?
When the user responds, how do I map that answer to the right part of the state (e.g. address, budget, etc.)?
Finally, is there a way to handle this in a more automatic way — so that resuming doesn’t require me to manually call Command(resume) each time, but instead flows naturally depending on which interrupt was triggered?