666ghj/MiroFish · error · RuntimeError
模拟停止失败
Error message
模拟停止失败
What it means
Generic RuntimeError raised by stop_simulation when, after termination, the (possibly refreshed) run state ends as FAILED but state.error is None/empty. It is a fallback message; normally the specific failure text is carried instead (see the branch above it that prefers state.error).
Source
Thrown at backend/app/services/simulation_runner.py:1077
RunnerStatus.FAILED,
state.error,
)
raise RuntimeError(state.error) from error
state.runner_status = RunnerStatus.STOPPED
state.twitter_running = False
state.reddit_running = False
state.completed_at = datetime.now().isoformat()
state.error = None
cls._save_run_state(state)
cls._sync_simulation_status(
simulation_id,
RunnerStatus.STOPPED,
)
cls._manual_stop_requests.discard(simulation_id)
state = cls.get_run_state(simulation_id) or state
if state.runner_status == RunnerStatus.FAILED:
raise RuntimeError(state.error or "模拟停止失败")
if state.runner_status != RunnerStatus.STOPPED:
raise RuntimeError(
f"模拟停止未达到终态: {simulation_id}, status={state.runner_status}"
)
logger.info(f"模拟已停止: {simulation_id}")
return state
@classmethod
def _read_actions_from_file(
cls,
file_path: str,
default_platform: Optional[str] = None,
platform_filter: Optional[str] = None,
agent_id: Optional[int] = None,
round_num: Optional[int] = None
) -> List[AgentAction]:
"""View on GitHub (pinned to b5b53acc57)
Solutions
- Search logs around the stop for the real cause (the monitor thread logged it before marking FAILED).
- Audit any code that assigns RunnerStatus.FAILED directly to also set state.error.
- Re-fetch the run state / simulation record for the persisted error message, which may not be in the in-memory copy.
Example fix
# before
state.runner_status = RunnerStatus.FAILED
cls._save_run_state(state) # later stop_simulation raises bare 模拟停止失败
# after
state.runner_status = RunnerStatus.FAILED
state.error = f"monitor exit code {proc.returncode}"
cls._save_run_state(state) Defensive patterns
Strategy: try-catch
Try / catch
try:
SimulationRunner.stop_simulation(sim_id)
except RuntimeError as e:
state = SimulationRunner.get_run_state(sim_id)
if state and state.runner_status == RunnerStatus.FAILED:
# real cause is in logs / persisted record, not the exception text
handle_failed_run(sim_id)
else:
raise Prevention
- Always set state.error whenever setting runner_status = FAILED.
- Log the failure cause at the point it is detected, not only at stop.
- Include machine-readable failure reasons in the persisted simulation record.
When it happens
Trigger: Stop completes, state.runner_status == RunnerStatus.FAILED, and state.error was cleared or never set — e.g. an external writer (monitor thread or crash handler) set FAILED without a message, or a race cleared state.error between the FAILED assignment and this read.
Common situations: Rare defensive path; usually appears after custom patches that set runner_status = FAILED directly, or after a restart-recovery routine that reconstructs state without the error field.
Related errors
- 模拟不存在: {simulation_id}
- 模拟未在运行: {simulation_id}, status={state.runner_status}
- 模拟仍在停止中,图谱写入未在 {wait_timeout:.0f}s 内完成
- Zep图谱写入未完整完成: {error}
- 模拟停止未达到终态: {simulation_id}, status={state.runner_status}
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/140d231b4f58f5c1.
Report an issue: GitHub.