From 4826222d4d58a5c719f06af9d1b0a5e65f56a962 Mon Sep 17 00:00:00 2001 From: Sydney Runkle Date: Mon, 9 Mar 2026 11:00:34 -0400 Subject: [PATCH] raise on update input --- libs/langgraph/langgraph/pregel/_loop.py | 8 ++++++++ libs/langgraph/tests/test_pregel.py | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/libs/langgraph/langgraph/pregel/_loop.py b/libs/langgraph/langgraph/pregel/_loop.py index 79201d0d6..32e47c4bd 100644 --- a/libs/langgraph/langgraph/pregel/_loop.py +++ b/libs/langgraph/langgraph/pregel/_loop.py @@ -637,6 +637,14 @@ class PregelLoop: # map command to writes if isinstance(self.input, Command): + if self.input.resume is not None and self.input.update: + raise ValueError( + "Command(resume=..., update=...) is not allowed as graph input. " + "Use Command(resume=...) to resume execution, or use " + "graph.update_state(...) to update state before resuming. " + "Note: Command(update=...) can still be used as a return " + "value from a node to update graph state." + ) if (resume := self.input.resume) is not None: if not self.checkpointer: raise RuntimeError( diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index 61eaa5116..5e19aa910 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -5850,6 +5850,30 @@ def test_task_before_interrupt_resume( assert result == {"answers": ["answer1", "answer2"]} +def test_command_resume_with_update_raises( + sync_checkpointer: BaseCheckpointSaver, +) -> None: + """Test that Command(resume=..., update=...) raises InvalidUpdateError + when used as graph input.""" + + @entrypoint(checkpointer=sync_checkpointer) + def workflow(inputs: str) -> str: + answer = interrupt("question") + return answer + + config = {"configurable": {"thread_id": "1"}} + + # First invocation triggers interrupt + result = workflow.invoke("start", config=config) + assert "__interrupt__" in result + + # Resuming with both resume and update should raise + with pytest.raises(ValueError, match="Command\\(resume=.*update="): + workflow.invoke( + Command(resume="answer", update={"foo": "bar"}), config=config + ) + + def test_multiple_tasks_before_interrupt_resume( sync_checkpointer: BaseCheckpointSaver, ) -> None: