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

  1. Stop or finish all running simulations for the project, then retry the graph delete.
  2. List active consumers from the error message (it names the active_simulations) and verify each one's real state.
  3. 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.
  4. 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

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


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/94d1b07c37619320. Report an issue: GitHub.