Skip to content

fix(config): fail startup when a static local bind cannot be opened - #2588

Merged
howardjohn merged 5 commits into
agentgateway:mainfrom
piyushbag:fix/static-config-bind-failure-fatal
Aug 4, 2026
Merged

howardjohn merged 5 commits into
agentgateway:mainfrom
piyushbag:fix/static-config-bind-failure-fatal

Conversation

@piyushbag

Copy link
Copy Markdown
Contributor

Problem

When running with a static local config (agentgateway -f config.json), a listener that could not open its OS socket (for example, the port was already in use) only logged a warning and the process kept running, serving nothing on that bind. As reported in #87, this is easy to miss: it looks like the gateway started fine, but the configured listeners are not actually accepting traffic.

Approach

  • Store::upsert_bind now returns the underlying bind error instead of swallowing it. The bind is still recorded in the store so routing lookups stay consistent.
  • sync_local (used only by the static local config path) collects any bind failures and returns them. The initial config load is awaited at startup, so the error propagates and the process exits non-zero.
  • XDS-delivered binds keep their previous behavior via insert_bind (log and continue), so a bad dynamic config does not crash a running proxy. Config file reloads also keep the last good state on error rather than exiting.

This mirrors the common proxy convention where a static listener that cannot bind is fatal, while dynamically delivered listeners are rejected without taking the process down.

Test plan

  • cargo test -p agentgateway --lib store::binds:: (adds sync_local_bind_failure_is_fatal and sync_local_bind_success_returns_ok; full module green)
  • cargo clippy -p agentgateway --all-targets (clean)
  • cargo fmt --check -- --config imports_granularity=Module,group_imports=StdExternalCrate,normalize_comments=true (clean)

Fixes #87

@piyushbag
piyushbag requested a review from a team as a code owner July 18, 2026 18:08
config.route_groups,
prev.binds,
);
)?;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will short-circuit the discovery sync which doesn't seem ideal. I am talking about on a new port, after the app has started

@piyushbag piyushbag Jul 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. The ? on sync_local for binds was returning before discovery sync ran.

Updated in e1d2d74:

  1. reload_config now always runs discovery sync, then propagates any bind error from the binds sync.
  2. sync_local only treats bind failures as fatal for newly added binds (keys not in the previous sync state). Existing binds are not re-opened, which matches the runtime case you described: a new port added after the app has started.

XDS-delivered binds still log-and-continue via insert_bind. Added tests for reload-with-new-bind failure and for config still applying when a bind fails.

piyushbag and others added 5 commits August 4, 2026 13:33
A static local config (`-f config.json`) that could not open a listener socket
(e.g. the port was already in use) only logged a warning and kept running,
serving nothing on that bind. This is confusing to operators and hides a fatal
misconfiguration.

`Store::upsert_bind` now returns the bind error instead of swallowing it.
`sync_local`, which is only used by the static local config path, collects these
errors and returns them so startup propagates the failure and exits non-zero.
XDS-delivered binds keep their previous behavior (log and continue) so a bad
dynamic config does not crash a running proxy, and config reloads keep the
previous good state on error.

Signed-off-by: Piyush Jagadish Bag <[email protected]>
Run discovery sync even when a static local bind cannot open, so workload
and service state are not skipped on reload. Only treat bind failures for
newly added binds as fatal; existing binds are not re-opened.

Signed-off-by: Piyush Jagadish Bag <[email protected]>
Apply rustfmt to the sync_local_new_bind_on_reload_failure_is_fatal
test bind vector so proxy-lint passes.

Signed-off-by: Piyush Jagadish Bag <[email protected]>
Prior run 30025848065: proxy-lint green; integration failed on
llm_model_router_handles_multipart_audio_detect_request with
[::]:4000 EADDRINUSE (environment), not a fmt/lint regression.

Signed-off-by: Piyush Jagadish Bag <[email protected]>
@howardjohn
howardjohn force-pushed the fix/static-config-bind-failure-fatal branch from a1a6073 to d108dd4 Compare August 4, 2026 20:38
@howardjohn
howardjohn merged commit 574d0b0 into agentgateway:main Aug 4, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Failed Bind Should Exit(1)

2 participants