666ghj/MiroFish · error · ValueError

模拟已在运行或结束处理中: {simulation_id}

Error message

模拟已在运行或结束处理中: {simulation_id}

What it means

Raised in SimulationRunner's start path under a per-simulation finalization lock: if existing run state has runner_status in {STARTING, RUNNING, PAUSED, STOPPING}, or ZepGraphMemoryManager still has an updater registered for the simulation, it raises ValueError('模拟已在运行或结束处理中'). This is a deliberate fail-closed concurrency guard against double-start and against racing the shutdown/finalization path.

Source

Thrown at backend/app/services/simulation_runner.py:437

            total_simulation_hours=total_hours,
            started_at=datetime.now().isoformat(),
        )
        
        # Atomically claim this simulation ID. The expensive updater/process
        # startup happens after releasing the lock, while the persisted
        # STARTING state makes every concurrent start fail closed.
        with cls._finalization_lock(simulation_id):
            existing = cls.get_run_state(simulation_id)
            active_statuses = {
                RunnerStatus.STARTING,
                RunnerStatus.RUNNING,
                RunnerStatus.PAUSED,
                RunnerStatus.STOPPING,
            }
            if (
                existing and existing.runner_status in active_statuses
            ) or ZepGraphMemoryManager.get_updater(simulation_id) is not None:
                raise ValueError(f"模拟已在运行或结束处理中: {simulation_id}")
            cls._save_run_state(state)
        
        # 如果启用图谱记忆更新,创建更新器
        if enable_graph_memory_update:
            if not graph_id:
                raise ValueError("启用图谱记忆更新时必须提供 graph_id")
            
            try:
                ZepGraphMemoryManager.create_updater(simulation_id, graph_id)
                cls._graph_memory_enabled[simulation_id] = True
                logger.info(f"已启用图谱记忆更新: simulation_id={simulation_id}, graph_id={graph_id}")
            except Exception as e:
                logger.error(f"创建图谱记忆更新器失败: {e}")
                cls._graph_memory_enabled[simulation_id] = False
                state.runner_status = RunnerStatus.FAILED
                state.error = f"Zep图谱更新器初始化失败: {e}"
                with cls._finalization_lock(simulation_id):
                    cls._save_run_state(state)

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Stop the existing run first (stop endpoint) and wait for runner_status to leave the active set before starting again.
  2. If the status is stale after a crash, stop/cleanup the run state (or reset runner_status to a terminal value via the recovery path) then start.
  3. For a PAUSED run, use the resume flow rather than start.
  4. If stuck in STOPPING, check and kill the simulation subprocess, then let finalization complete.

Example fix

# before
SimulationRunner.start_simulation(sim_id, ...)
# after
state = SimulationRunner.get_run_state(sim_id)
if state and state.runner_status in {'STARTING', 'RUNNING', 'PAUSED', 'STOPPING'}:
    SimulationRunner.stop_simulation(sim_id)
    # wait for terminal status / cleanup, then start
SimulationRunner.start_simulation(sim_id, ...)
Defensive patterns

Strategy: validation

Validate before calling

active = {'STARTING', 'RUNNING', 'PAUSED', 'STOPPING'}
state = SimulationRunner.get_run_state(sim_id)
if state and state.runner_status in active:
    SimulationRunner.stop_simulation(sim_id)  # or resume if PAUSED
    wait_for_terminal_status(sim_id)

Try / catch

try:
    SimulationRunner.start_simulation(sim_id, ...)
except ValueError as e:
    if '已在运行' in str(e):
        raise HTTPException(409, str(e)) from e  # conflict: stop or resume first
    raise

Prevention

When it happens

Trigger: Calling start twice (double-click, frontend retry, duplicate webhook); starting while a previous run is PAUSED or mid-STOPPING; a stale run state left RUNNING after a crash or backend kill -9; an orphaned ZepGraphMemoryManager updater left behind by a failed prior stop.

Common situations: Backend crashed without finalizing state, leaving status RUNNING on disk; user resumes from a paused run by calling start instead of resume; two API replicas both accepting a start request; STOPPING stuck because the subprocess is hung.

Related errors


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