invoke-ai/InvokeAI · error · NodeAlreadyExecutedError
Node {node_id} has already been prepared or executed and can
Error message
Node {node_id} has already been prepared or executed and cannot be deleted What it means
GraphExecution throws NodeAlreadyExecutedError when deleting a node from a graph that has been prepared or executed. InvokeAI freezes executed graphs so in-flight or completed queue items cannot be mutated. Delete the node before the graph is queued, or work on a fresh copy of the graph.
Source
Thrown at invokeai/app/services/shared/graph.py:2860
return True
def _is_node_updatable(self, node_id: str) -> bool:
# The node is updatable as long as it hasn't been prepared or executed
return node_id not in self.source_prepared_mapping
def add_node(self, node: BaseInvocation) -> None:
self.graph.add_node(node)
def update_node(self, node_id: str, new_node: BaseInvocation) -> None:
if not self._is_node_updatable(node_id):
raise NodeAlreadyExecutedError(
f"Node {node_id} has already been prepared or executed and cannot be updated"
)
self.graph.update_node(node_id, new_node)
def delete_node(self, node_id: str) -> None:
if not self._is_node_updatable(node_id):
raise NodeAlreadyExecutedError(
f"Node {node_id} has already been prepared or executed and cannot be deleted"
)
self.graph.delete_node(node_id)
def add_edge(self, edge: Edge) -> None:
if not self._is_node_updatable(edge.destination.node_id):
raise NodeAlreadyExecutedError(
f"Destination node {edge.destination.node_id} has already been prepared or executed and cannot be linked to"
)
self.graph.add_edge(edge)
def delete_edge(self, edge: Edge) -> None:
if not self._is_node_updatable(edge.destination.node_id):
raise NodeAlreadyExecutedError(
f"Destination node {edge.destination.node_id} has already been prepared or executed and cannot have a source edge deleted"
)
self.graph.delete_edge(edge)
View on GitHub (pinned to 0b6a024f2f)
Solutions
- Delete the node before adding the graph to the queue, not after execution begins
- Clone/copy the graph, mutate the copy, and enqueue a new queue item instead of mutating the executed one
- Catch NodeAlreadyExecutedError and skip or log when the node is no longer needed
- Check _is_node_updatable (or track node state) before calling delete_node
Example fix
// before
execution.delete_node("node-1") # raises if already run
// after
if execution._is_node_updatable("node-1"):
execution.delete_node("node-1")
else:
# mutate a fresh copy and enqueue a new item
new_graph = execution._graph.model_copy(deep=True) Defensive patterns
Strategy: validation
Validate before calling
if not execution._is_node_updatable(node_id):
raise SkipMutation(f"node {node_id} already executed")
execution.delete_node(node_id) Type guard
def can_mutate(execution, node_id: str) -> bool:
return execution._is_node_updatable(node_id) Try / catch
try:
execution.delete_node(node_id)
except NodeAlreadyExecutedError as e:
logger.warning("skipped delete on executed graph: %s", e) Prevention
- Mutate graphs only before enqueueing
- Work on deep-copied graphs for late edits
- Track execution state in your editing UI
- Wrap dynamic edits in try/except NodeAlreadyExecutedError
When it happens
Trigger: Calling GraphExecution.delete_node(node_id) after self._is_node_updatable(node_id) returns False, i.e. the node is already in the prepared/executed node list of the running session.
Common situations: Dynamic graph editing APIs (node deleted via websocket/API mid-run), a batch workflow that tries to prune nodes after execution started, or reusing a cached GraphExecution object across runs.
Related errors
- Destination node {edge.destination.node_id} has already been
- Destination node {edge.destination.node_id} has already been
- call_saved_workflow exceeds remaining queue capacity for chi
- Workflow call did not produce any child executions.
- Node ids must match, got {node_dict_id} and {node.id}
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/d4250d166da627f5.
Report an issue: GitHub.