{"record":{"id":"964993d5aff7ba90","repo":"666ghj/MiroFish","slug":"script-path","errorCode":null,"errorMessage":"脚本不存在: {script_path}","messagePattern":"脚本不存在: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/app/services/simulation_runner.py","lineNumber":500,"sourceCode":"                try:\n                    ZepGraphMemoryManager.stop_updater(simulation_id)\n                    cls._graph_memory_enabled.pop(simulation_id, None)\n                except Exception as error:\n                    cleanup_error = error\n            state.runner_status = RunnerStatus.FAILED\n            state.twitter_running = False\n            state.reddit_running = False\n            state.error = f\"脚本不存在: {script_path}\"\n            if cleanup_error is not None:\n                state.error += f\"; Zep图谱写入清理失败: {cleanup_error}\"\n            with cls._finalization_lock(simulation_id):\n                cls._save_run_state(state)\n                cls._sync_simulation_status(\n                    simulation_id,\n                    RunnerStatus.FAILED,\n                    state.error,\n                )\n            raise ValueError(state.error)\n        \n        # 创建动作队列\n        action_queue = Queue()\n        cls._action_queues[simulation_id] = action_queue\n\n        process = None\n        main_log_file = None\n\n        # 启动模拟进程\n        try:\n            # 构建运行命令，使用完整路径\n            # 新的日志结构：\n            #   twitter/actions.jsonl - Twitter 动作日志\n            #   reddit/actions.jsonl  - Reddit 动作日志\n            #   simulation.log        - 主进程日志\n            \n            cmd = [\n                sys.executable,  # Python解释器","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/backend/app/services/simulation_runner.py#L482-L518","documentation":"Raised by SimulationRunner.start_simulation when the OASIS simulation script path passed in the config does not exist on disk. Before raising, the runner marks the run FAILED, clears twitter/reddit running flags, appends any Zep graph-write cleanup failure to the message, persists run state, and syncs status. The ValueError surfaces the composed state.error string to the caller.","triggerScenarios":"Calling start_simulation (or the API endpoint that wraps it) with a config whose script_path points to a missing/unmounted file; the runner builds the full path from config and the os.path/existence precheck fails. The message may also carry '; Zep图谱写入清理失败: ...' when the cleanup of a previously started graph updater also errored.","commonSituations":"Relative script paths resolved against the wrong CWD (backend started from repo root vs backend/), renamed or moved simulation scripts, deployment where the scripts directory is not shipped or mounted, or a stale config JSON referencing an old script name.","solutions":["Verify the exact path in the error message exists on the machine running the backend (ls the path from the message).","Make script_path absolute (or resolve it against a project-configured base directory) before calling start_simulation.","Re-generate the simulation config so script_path matches the current script location shipped with the deployment.","If the '; Zep图谱写入清理失败' suffix appears, also fix the Zep connection/key issue reported there — the run failed for both reasons."],"exampleFix":"// before\nconfig.script_path = \"scripts/run_twitter_simulation.py\"; // resolved from unknown CWD\n\n// after\nimport app_config\nconfig.script_path = str(\n    (Path(app_config.PROJECT_ROOT) / \"scripts\" / \"run_twitter_simulation.py\").resolve()\n);\nif not Path(config.script_path).exists():\n    raise FileNotFoundError(config.script_path);","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef can_start(config) -> tuple[bool, str]:\n    p = Path(config.script_path)\n    if not p.is_absolute():\n        return False, \"script_path must be absolute\"\n    if not p.is_file():\n        return False, f\"script missing: {p}\"\n    return True, \"\"","typeGuard":"def is_startable_config(config) -> bool:\n    p = Path(getattr(config, \"script_path\", \"\"))\n    return p.is_absolute() and p.is_file()","tryCatchPattern":"try:\n    SimulationRunner.start_simulation(cfg)\nexcept ValueError as e:\n    if str(e).startswith(\"脚本不存在\"):\n        # surface as config error, not server error\n        raise HTTPException(400, detail=str(e))\n    raise","preventionTips":["Store absolute script paths in generated configs.","Fail CI/deploy checks when the scripts directory is absent from the artifact.","Validate script_path existence in the config-generation step, not only at start."],"tags":["simulation","file-not-found","path-config","oasis"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}