666ghj/MiroFish · warning · ValueError

模拟未在运行: {simulation_id}, status={state.runner_status}

Error message

模拟未在运行: {simulation_id}, status={state.runner_status}

What it means

stop_simulation refuses to stop a run whose runner_status is a terminal/inactive state (anything other than STARTING, RUNNING, PAUSED, STOPPING) and that is not eligible for finalization retry (no pending Zep updater). This is a lifecycle guard against double-stop and against stopping runs that never really started.

Source

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

            pending_updater = ZepGraphMemoryManager.get_updater(simulation_id)
            retrying_finalization = (
                pending_updater is not None
                and state.runner_status in {
                    RunnerStatus.STOPPING,
                    RunnerStatus.FAILED,
                }
            )
            if (
                state.runner_status not in [
                    RunnerStatus.STARTING,
                    RunnerStatus.RUNNING,
                    RunnerStatus.PAUSED,
                    RunnerStatus.STOPPING,
                ]
                and not retrying_finalization
            ):
                raise ValueError(
                    f"模拟未在运行: {simulation_id}, status={state.runner_status}"
                )

            state.runner_status = RunnerStatus.STOPPING
            cls._manual_stop_requests.add(simulation_id)
            cls._save_run_state(state)
            cls._sync_simulation_status(simulation_id, RunnerStatus.STOPPING)

            # 终止进程
            process = cls._processes.get(simulation_id)
            if process and process.poll() is None:
                try:
                    cls._terminate_process(process, simulation_id)
                except ProcessLookupError:
                    pass
                except Exception as e:
                    logger.error(f"终止进程组失败: {simulation_id}, error={e}")
                    try:

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Refresh runner_status via get_run_state / the status endpoint immediately before calling stop, and skip stop for STOPPED/FAILED/terminal states.
  2. Make the client idempotent: catch this ValueError and re-read status; if terminal, treat the stop as satisfied.
  3. Guard the Stop button in the UI against states other than STARTING/RUNNING/PAUSED.

Example fix

// before
try:
    SimulationRunner.stop_simulation(sim_id)
except ValueError as e:
    raise  # surfaces 模拟未在运行 to the user

// after
ACTIVE = {RunnerStatus.STARTING, RunnerStatus.RUNNING, RunnerStatus.PAUSED, RunnerStatus.STOPPING}
state = SimulationRunner.get_run_state(sim_id)
if state and state.runner_status not in ACTIVE:
    return state  # already terminal, nothing to stop
SimulationRunner.stop_simulation(sim_id);
Defensive patterns

Strategy: validation

Validate before calling

ACTIVE_STOP_STATES = {
    RunnerStatus.STARTING, RunnerStatus.RUNNING,
    RunnerStatus.PAUSED, RunnerStatus.STOPPING,
}

def should_request_stop(sim_id: str) -> bool:
    s = SimulationRunner.get_run_state(sim_id)
    return s is not None and s.runner_status in ACTIVE_STOP_STATES

Try / catch

try:
    SimulationRunner.stop_simulation(sim_id)
except ValueError as e:
    if "模拟未在运行" in str(e):
        return SimulationRunner.get_run_state(sim_id)  # already terminal
    raise

Prevention

When it happens

Trigger: Calling stop on a run already STOPPED or FAILED (and retrying_finalization is False because there is no pending ZepGraphMemoryManager updater); racing with the monitor thread that just finalized the run between your get_run_state check and the stop call.

Common situations: Frontend 'Stop' button not disabled when the status poll shows a terminal state; two concurrent stop requests; retry of a timed-out stop request after the run already finalized.

Related errors


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