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
- Refresh runner_status via get_run_state / the status endpoint immediately before calling stop, and skip stop for STOPPED/FAILED/terminal states.
- Make the client idempotent: catch this ValueError and re-read status; if terminal, treat the stop as satisfied.
- 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
- Disable the Stop UI action for terminal statuses.
- Re-check status immediately before issuing stop to shrink the race window.
- Return 409 Conflict from the API layer for this error so clients can react.
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
- 模拟停止未达到终态: {simulation_id}, status={state.runner_status}
- 模拟已在运行或结束处理中: {simulation_id}
- 模拟不存在: {simulation_id}
- 模拟仍在停止中,图谱写入未在 {wait_timeout:.0f}s 内完成
- Zep图谱写入未完整完成: {error}
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/5c6c812d3f1800b3.
Report an issue: GitHub.