Skip to content

standalone hindsight-api does not fail fast on port conflict — full init (embedded PG + models + migration) runs before [Errno 98] bind failure #4281

Description

@j0935586110-lgtm

Bug Description

The standalone hindsight-api CLI does not fail fast on a port conflict. When the target port is already occupied, it performs the entire expensive startup — starts the embedded PostgreSQL, loads the embedding and reranker models, verifies the LLM connection, and runs database migrations — and only afterwards does uvicorn attempt to bind the socket and fail with [Errno 98] address already in use.

Under a process supervisor with Restart=always (systemd, docker restart policy, etc.) this becomes a restart storm: each ~11s cycle does a full init + graceful shutdown, then restarts ~5s later, wasting CPU/memory and — because the embedded PostgreSQL uses port=auto — starting a fresh PG instance on an incrementing port each cycle, which can leave orphan postgres processes behind.

Environment

  • hindsight-all 0.9.2 (hindsight-api standalone entry point)
  • Linux, systemd user service Restart=always, ExecStart=hindsight-api --host 127.0.0.1 --port 9177
  • The port is held by another Hindsight daemon (e.g. one spawned via HindsightEmbedded)

Steps to Reproduce

  1. Start any listener on 127.0.0.1:9177 (e.g. a second Hindsight daemon).
  2. Run hindsight-api --host 127.0.0.1 --port 9177.

Expected Behavior

The CLI should check port availability at the very start (before instantiating MemoryEngine, starting the embedded PG, loading models, or running migrations) and exit immediately with a clear error when the port is unavailable.

Actual Behavior

Full initialization runs first, then the bind fails:

... Starting Hindsight API...
INFO:     Waiting for application startup.
... Embeddings: initializing local provider with model BAAI/bge-small-en-v1.5
... Reranker: initializing local provider with model cross-encoder/ms-marco-MiniLM-L-6-v2
... Starting embedded PostgreSQL (name=hindsight, port=auto)...
... PostgreSQL started: postgresql://hindsight:***@127.0.0.1:5433/hindsight
... Running database migrations...
... Database migrations completed successfully for schema 'public'
... Memory system initialized (pool and task backend started)
INFO:     Application startup complete.
ERROR:    [Errno 98] error while attempting to bind on address ('127.0.0.1', 9177): address already in use
INFO:     Waiting for application shutdown.
... Stopping pg0...

Root cause

hindsight_api/server.py instantiates MemoryEngine(...) and create_app(...) at module import time; main.py only calls uvicorn.run() afterwards, and the socket bind happens after the app (and its lifespan startup) has fully initialized. There is no port-availability pre-flight check on the standalone path.

Contrast with embedded mode

The embedded path (hindsight_embed/daemon_embed_manager.py) already guards against this: _clear_port() / _is_port_in_use() run before spawning a daemon (handling healthy-reuse / stale-reclaim / foreign-refuse — see #843, #3520, #3100). The standalone hindsight-api CLI has no equivalent pre-flight check.

Suggested fix

Perform the same loopback port check (_is_port_in_use) at the top of the standalone CLI startup (before MemoryEngine / migrations), and exit with a clear, actionable error when the port is occupied.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions