666ghj/MiroFish · error · GraphInUseError
Graph {graph_id} is in use by active consumer(s): {', '.join
Error message
Graph {graph_id} is in use by active consumer(s): {', '.join(active_simulations)} What it means
GraphInUseError raised in _delete_cloud_graph_if_present (backend/app/api/graph.py) when a project's referenced Zep Cloud graph is about to be deleted but _active_graph_consumers still reports simulations using that graph_id. Deletion happens inside graph_lifecycle_lock(graph_id) so the consumer check and the Cloud DELETE are atomic per graph, preventing a race where a simulation starts querying a graph mid-deletion.
Source
Thrown at backend/app/api/graph.py:87
continue
run_state = SimulationRunner.get_run_state(simulation.simulation_id)
if run_state and run_state.runner_status in active_runner_statuses:
active.add(simulation.simulation_id)
return sorted(active)
def _delete_cloud_graph_if_present(graph_id: str | None) -> None:
"""Delete a referenced Cloud graph without retrying the mutation."""
if not graph_id:
return
# Keep the consumer check and Cloud mutation in one critical section. The
# callers that also clear local references hold this re-entrant lock around
# both operations.
with graph_lifecycle_lock(graph_id):
active_simulations = _active_graph_consumers(graph_id)
if active_simulations:
raise GraphInUseError(
f"Graph {graph_id} is in use by active consumer(s): "
f"{', '.join(active_simulations)}"
)
try:
GraphBuilderService(api_key=Config.ZEP_API_KEY).delete_graph(graph_id)
except NotFoundError:
logger.info("Zep Cloud graph already absent: %s", graph_id)
def _clear_project_graph_reference(project) -> None:
project.graph_id = None
project.graph_build_task_id = None
project.zep_batch_id = None
project.zep_batch_operation_id = None
project.error = None
def _project_build_lock(project_id: str) -> threading.Lock:View on GitHub (pinned to b5b53acc57)
Solutions
- Stop or finish all running simulations for the project, then retry the graph delete.
- List active consumers from the error message (it names the active_simulations) and verify each one's real state.
- If a simulation is genuinely orphaned (worker died but DB row says active), mark it terminated/cleaned in the task manager so _active_graph_consumers stops counting it.
- For rebuild flows, wire the UI to stop simulations automatically before requesting deletion instead of surfacing this error to users.
Example fix
# before
@router.delete("/projects/{project_id}/graph")
def delete_graph(project_id: str):
_delete_cloud_graph_if_present(project.graph_id) # raises GraphInUseError to the client
# after - stop consumers first, then delete inside the same lifecycle domain
@router.delete("/projects/{project_id}/graph")
def delete_graph(project_id: str):
active = _active_graph_consumers(project.graph_id)
if active:
for sim_id in active:
simulation_service.stop(sim_id) # or return 409 with the consumer list
_delete_cloud_graph_if_present(project.graph_id) Defensive patterns
Strategy: validation
Validate before calling
active = _active_graph_consumers(project.graph_id)
if active:
# either stop them or refuse with a 409 payload naming them
return {"detail": {"code": "graph_in_use", "consumers": active}} Try / catch
try:
_delete_cloud_graph_if_present(project.graph_id)
except GraphInUseError as e:
raise HTTPException(status_code=409, detail=str(e)) from e Prevention
- Always stop simulations before issuing graph delete/rebuild; make the UI orchestrate stop-then-delete.
- Tear down simulations in test fixtures before deleting graphs.
- Reconcile zombie 'active' simulations after worker crashes so they stop blocking deletes.
- Return 409 with the consumer list so clients can present actionable choices.
When it happens
Trigger: Calling the graph delete/rebuild endpoint while any simulation for the project is still running (active simulations hold the graph_id); concurrent delete requests for the same graph (second one blocks on the re-entrant lock and re-checks consumers); a simulation stuck in an active state after a crashed worker so it never releases the graph.
Common situations: User hits 'delete graph' or 'rebuild' from the UI while Step5 simulations are open; orphaned/zombie simulation tasks after a backend restart keep the graph marked in-use; test suites that create simulations and delete graphs without tearing down simulations first.
Related errors
- Persisted Zep batch does not match the current graph input
- ZEP_API_KEY 未配置
- Zep batch list cursor did not advance
- Multiple Zep batches match operation {operation_id}; refusin
- graph_id is required
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/94d1b07c37619320.
Report an issue: GitHub.