666ghj/MiroFish · error · ValueError

模拟配置不存在,请先调用 /prepare 接口

Error message

模拟配置不存在,请先调用 /prepare 接口

What it means

Raised in SimulationRunner (class method starting a run): it looks for simulation_config.json under the run-state directory for the simulation id, and if the file is absent raises ValueError telling the user to call /prepare first. The run stage depends on artifacts (config JSON, profiles) produced by the earlier prepare stage; running without them is a workflow-order violation.

Source

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

        """
        启动模拟
        
        Args:
            simulation_id: 模拟ID
            platform: 运行平台 (twitter/reddit/parallel)
            max_rounds: 最大模拟轮数(可选,用于截断过长的模拟)
            enable_graph_memory_update: 是否将Agent活动动态更新到Zep图谱
            graph_id: Zep图谱ID(启用图谱更新时必需)
            
        Returns:
            SimulationRunState
        """
        # 加载模拟配置
        sim_dir = os.path.join(cls.RUN_STATE_DIR, simulation_id)
        config_path = os.path.join(sim_dir, "simulation_config.json")
        
        if not os.path.exists(config_path):
            raise ValueError(f"模拟配置不存在,请先调用 /prepare 接口")
        
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
        
        # 初始化运行状态
        time_config = config.get("time_config", {})
        total_hours = time_config.get("total_simulation_hours", 72)
        minutes_per_round = time_config.get("minutes_per_round", 30)
        total_rounds = int(total_hours * 60 / minutes_per_round)
        
        # 如果指定了最大轮数,则截断
        if max_rounds is not None and max_rounds > 0:
            original_rounds = total_rounds
            total_rounds = min(total_rounds, max_rounds)
            if total_rounds < original_rounds:
                logger.info(f"轮数已截断: {original_rounds} -> {total_rounds} (max_rounds={max_rounds})")
        
        state = SimulationRunState(

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Call the /prepare endpoint for this simulation and wait for success before starting the run.
  2. If prepare failed, read its error (state.error) and fix the underlying cause first (often errors 32/33/30).
  3. Verify simulation_config.json exists under the runner's RUN_STATE_DIR/<simulation_id>/; if the directory moved, align prepare and runner paths.
  4. In the UI, disable the run action until prepare reports success.

Example fix

# before
runner.start_simulation(sim_id, ...)  # ValueError: 模拟配置不存在
# after
config_path = os.path.join(SimulationRunner.RUN_STATE_DIR, sim_id, 'simulation_config.json')
if not os.path.exists(config_path):
    manager.prepare_simulation(sim_id, ...)  # produce config first
runner.start_simulation(sim_id, ...)
Defensive patterns

Strategy: validation

Validate before calling

config_path = os.path.join(SimulationRunner.RUN_STATE_DIR, simulation_id, 'simulation_config.json')
if not os.path.exists(config_path):
    raise HTTPException(409, 'prepare must complete before starting the run')

Try / catch

try:
    SimulationRunner.start_simulation(sim_id, ...)
except ValueError as e:
    if '请先调用 /prepare' in str(e):
        raise HTTPException(409, str(e)) from e
    raise

Prevention

When it happens

Trigger: Calling start/run on a simulation whose prepare never completed or failed (e.g. failed at error 33); prepare wrote config to a different directory than RUN_STATE_DIR/simulation_id; state directory reset between prepare and run; wrong simulation_id.

Common situations: Frontend lets the user press 'run' before prepare finishes; prepare failed silently and the user retried the wrong stage; container/volume mismatch losing prepared artifacts; concurrent env reset.

Related errors


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