ZhuLinsen/daily_stock_analysis · critical · RuntimeError

FastAPI 服务器启动后立即退出: {host}:{port}

Error message

FastAPI 服务器启动后立即退出: {host}:{port}

What it means

After the wait loop, the uvicorn server thread is dead, no exception was captured, and uvicorn_server.started is False — the process exited silently right after launch. Usually uvicorn received a shutdown signal, install-level signal handlers interfered, or run_server returned without raising (swallowed exception path).

Source

Thrown at main.py:1272

    while time.time() < wait_deadline:
        if startup_error:
            raise RuntimeError(
                f"FastAPI server failed to start: {host}:{port}; {startup_error[0]}"
            )
        if uvicorn_server.started:
            logger.info(f"FastAPI 服务已启动: http://{host}:{port}")
            return
        if not thread.is_alive():
            break
        time.sleep(0.05)

    if startup_error:
        raise RuntimeError(f"FastAPI server failed to start: {host}:{port}; {startup_error[0]}")
    if uvicorn_server.started:
        logger.info(f"FastAPI 服务已启动: http://{host}:{port}")
        return
    if not thread.is_alive():
        raise RuntimeError(f"FastAPI 服务器启动后立即退出: {host}:{port}")

    raise RuntimeError(f"FastAPI 服务在 {timeout_seconds:.1f}s 内未完成启动: {host}:{port}")


def _is_truthy_env(var_name: str, default: str = "true") -> bool:
    """Parse common truthy / falsy environment values."""
    value = os.getenv(var_name, default).strip().lower()
    return value not in {"0", "false", "no", "off"}


def start_bot_stream_clients(config: Config) -> None:
    """Start bot stream clients when enabled in config."""
    # 启动钉钉 Stream 客户端
    if config.dingtalk_stream_enabled:
        try:
            from bot.platforms import start_dingtalk_stream_background, DINGTALK_STREAM_AVAILABLE
            if DINGTALK_STREAM_AVAILABLE:
                if start_dingtalk_stream_background():

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Check whether anything sends signals to the process at startup (supervisor, docker stop, CI timeout) and delay or disable that.
  2. Verify the imported ASGI app is a real object: python -c "import server; print(server.app)".
  3. Broaden the except in run_server to BaseException (or add logging on thread exit via threading.excepthook) so silent deaths become diagnosable.
  4. Retry once after clearing the environment (stray SIGINT from a terminal job-control issue).

Example fix

# before
    def run_server():
        try:
            uvicorn_server.run()
        except Exception as exc:  # SystemExit/KeyboardInterrupt vanish silently
            startup_error.append(exc)

# after
    def run_server():
        try:
            uvicorn_server.run()
        except BaseException as exc:  # capture silent thread deaths
            startup_error.append(exc)
Defensive patterns

Strategy: try-catch

Validate before calling

import threading
threading.excepthook = lambda args: logger.error("thread %s died: %s", args.thread.name, args.exc_value)  # install before server start

Try / catch

try:
    run_fastapi(host, port, config)
except RuntimeError as e:
    if "立即退出" in str(e):
        logger.error("server thread died silently; check signals and app import")
        subprocess.run([sys.executable, "-c", "import server"], check=True)  # preflight
        raise

Prevention

When it happens

Trigger: SIGTERM/SIGINT reaching the daemon thread during startup; use_config_signal_handlers interacting badly with the environment; run_server exiting normally because uvicorn_server.run() returned immediately (e.g. empty/None app passed after an import returned None); an exception type not derived from Exception (SystemExit, KeyboardInterrupt) bypassing the except clause and killing the thread silently.

Common situations: Running under a supervisor or test harness that delivers signals at startup; the app object import returning None; KeyboardInterrupt during the 3s startup window.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/1a07a85ec218698c. Report an issue: GitHub.