Found by the merge review of #962 (PRs #964/#963), not fixed there — the fix needs state on the
gateway, which is not a change to make mid-review.
What
LlmGateway.local_runtime_state() logs a WARNING every time it probes an unreachable runtime
(services/core-app/src/epicurus_core_app/llm/gateway.py:352). Since #962,
ModelBootstrap._wait_for_runtime polls that method rather than catching the exception itself
(llm/bootstrap.py:167-174), at poll_interval_s=5.0 for up to ready_timeout_s=180.0.
So a cold start where Ollama is simply still coming up — the default install path, where
core-app depends on ollama only with service_started — can emit up to 36
local runtime unreachable WARNING lines before the runtime binds. The previous code caught the
exception silently in the loop and logged a single debug line at the deadline.
In practice Ollama binds in a second or two, so it is usually 0–1 lines. It is a cold-start log
storm waiting for a slow disk, a big image pull, or a constrained box.
Why it matters beyond noise
WARNING is the level an operator's alerting is most likely to be wired to, and this fires on a
perfectly healthy boot. It also makes the genuinely-unreachable case (a configured runtime that
is really down) indistinguishable from the transient one, because both look like a stream of
identical warnings.
Shape of the fix
Lane A's own closing note already proposed the right treatment: warn once per streak, the way
the catalog does — log the first transition into unreachable and stay quiet until the state
changes back. That covers both this and the page-poll case (a Models page open against a down
runtime warns once per poll today).
Wants a small amount of state on the gateway (last observed state per URL), so it is its own
change with its own core-app bump, not a docs-only follow-up.
Found by the merge review of #962 (PRs #964/#963), not fixed there — the fix needs state on the
gateway, which is not a change to make mid-review.
What
LlmGateway.local_runtime_state()logs a WARNING every time it probes an unreachable runtime(
services/core-app/src/epicurus_core_app/llm/gateway.py:352). Since #962,ModelBootstrap._wait_for_runtimepolls that method rather than catching the exception itself(
llm/bootstrap.py:167-174), atpoll_interval_s=5.0for up toready_timeout_s=180.0.So a cold start where Ollama is simply still coming up — the default install path, where
core-appdepends onollamaonly withservice_started— can emit up to 36local runtime unreachableWARNING lines before the runtime binds. The previous code caught theexception silently in the loop and logged a single
debugline at the deadline.In practice Ollama binds in a second or two, so it is usually 0–1 lines. It is a cold-start log
storm waiting for a slow disk, a big image pull, or a constrained box.
Why it matters beyond noise
WARNING is the level an operator's alerting is most likely to be wired to, and this fires on a
perfectly healthy boot. It also makes the genuinely-unreachable case (a configured runtime that
is really down) indistinguishable from the transient one, because both look like a stream of
identical warnings.
Shape of the fix
Lane A's own closing note already proposed the right treatment: warn once per streak, the way
the catalog does — log the first transition into
unreachableand stay quiet until the statechanges back. That covers both this and the page-poll case (a Models page open against a down
runtime warns once per poll today).
Wants a small amount of state on the gateway (last observed state per URL), so it is its own
change with its own
core-appbump, not a docs-only follow-up.