Skip to content

fix(gateway): treat a blank GATEWAY_HOST/GATEWAY_PORT as unset - #5801

Merged
WillemJiang merged 1 commit into
bytedance:mainfrom
Lesereingrape:fix/gateway-blank-port-env
Sep 24, 2026
Merged

WillemJiang merged 1 commit into
bytedance:mainfrom
Lesereingrape:fix/gateway-blank-port-env

Conversation

@Lesereingrape

Copy link
Copy Markdown
Contributor

Fixes #5800

Why

Trigger: while reading the gateway startup path for another change I noticed that
get_gateway_config() passes its fallbacks as os.getenv defaults, which only apply when the
variable is absent — and this function is called at import time (app.py:1074 → create_app() →
app.py:711).

Pain: docker run -e GATEWAY_PORT=, or GATEWAY_PORT=${PORT} with PORT unset, hands us "", and
int("") raises ValueError: invalid literal for int() with base 10: ''. The whole gateway process
dies before uvicorn binds, even though uvicorn is what actually picks the listening port and this
config only feeds the startup log line at app.py:348 plus the bind address create_app() uses. A
variable that is set-but-empty means "not configured" everywhere else in this app
(app/channels/dedupe_store.py:204 does int(os.environ.get("GATEWAY_WORKERS", "1") or 1), and
app/gateway/deps.py:83-86 catches the same ValueError), so the failure is also inconsistent with
the repo's own convention.

What changed

  • A blank GATEWAY_HOST or GATEWAY_PORT now falls back to the declared default (0.0.0.0 /
    8001) instead of crashing the import, so an empty env var in a compose file or a shell
    expansion is treated the same as an absent one.
  • GATEWAY_PORT=0 still means port 0 (ask the OS for a free port) — the fallback is applied to
    the string before int(), not to the parsed number.
  • GATEWAY_ENABLE_DOCS is deliberately untouched: for that flag "" is a value a caller can
    reasonably mean as "off", and it fails closed today rather than crashing, so there is no bug to
    fix there.

Surface area

  • Backend API — endpoint / SSE event / request-response shape under backend/app
  • Default behavior change — changes existing behavior without the user opting in (default model, default setting, data shape)

Only the empty-value case changes: for host it goes from "" (an unusable bind address) to
0.0.0.0, and for port from a hard ValueError to 8001. Every non-empty value parses exactly as
before.

Bug fix verification

  • Test path that reproduces the bug: backend/tests/test_gateway_docs_toggle.py::test_blank_host_and_port_fall_back_to_defaults
  • Did it go red on main and green on this branch? yes
# tests/test_gateway_docs_toggle.py with app/gateway/config.py reverted to origin/main (f6269fb6)
1 failed, 12 passed     -> ValueError: invalid literal for int() with base 10: ''
# same file on this branch
13 passed

test_zero_port_is_not_treated_as_unset is the control that keeps the or fallback honest about
"0"; it passes in both columns, which is the point — it pins the case a naive int(x or 8001)
would get wrong.

Validation

make is not available on this machine, so I ran the commands behind make lint / make test
directly from backend/, with PYTHONPATH=.;packages/harness;packages/extension-api against a
Python 3.12 venv:

ruff check app/gateway/config.py tests/test_gateway_docs_toggle.py   -> All checks passed!
ruff format --check <same two files>                                 -> 2 files already formatted
pytest tests/test_gateway_docs_toggle.py                             -> 13 passed
pytest tests/test_gateway_lifespan_shutdown.py                       -> 14 passed   (reads get_gateway_config, via patch)

The full make test suite was not run locally; CI is the first full pass.

Notes for the reviewer

app/gateway/__init__.py re-exports get_gateway_config, so the module import in uvicorn app.gateway.app:app is the first thing that touches this — hence "at import time" rather than "at
first request".

AI assistance

Tool(s) used: Qoder agent (Claude), operating this account autonomously.

How you used it: the agent located the asymmetry, wrote the failing tests, the fix and this
description, and measured the red/green numbers above. The checkbox below is left unticked on
purpose: no human has read the diff yet, so nobody can claim that responsibility for it at the
moment of opening. The agent will amend or close the PR on request.

os.getenv's default only covers an unset variable, so `docker run -e
GATEWAY_PORT=` — or `GATEWAY_PORT=${PORT}` with PORT unset — reached
int("") and aborted the create_app() import that both entry points run.
Blank now falls back to the declared default, while "0" stays a real port.
@WillemJiang
WillemJiang marked this pull request as ready for review September 24, 2026 07:39
@github-actions github-actions Bot added area:backend Gateway / runtime / core backend under backend/ needs-validation Touches front/back contract surface; needs real-path validation risk:high High risk: backend API, agents, sandbox, auth, deps, CI size/S PR changes 20-100 lines labels Sep 24, 2026
@WillemJiang WillemJiang added this to the 2.2.0 milestone Sep 24, 2026
@WillemJiang
WillemJiang merged commit 2c43767 into bytedance:main Sep 24, 2026
25 checks passed
WillemJiang pushed a commit that referenced this pull request Sep 25, 2026
…out as unset (#5840)

Follows #5801 for the remaining numeric env reads that break on an empty
value (`-e VAR=`, or `${VAR}` with VAR unset):

- The WeChat QR worker guard read a blank GATEWAY_WORKERS as an invalid
  count and refused QR login, although the compose command starts one
  worker for it (`--workers ${GATEWAY_WORKERS:-1}`) and the other worker
  checks treat it as 1. A blank value now falls through to WEB_CONCURRENCY
  and then 1, so the guard still fails closed for real multi-worker setups.
- The Lark broker aborted startup on int("") for a blank
  DEERFLOW_LARK_BROKER_PORT or DEERFLOW_LARK_BROKER_TIMEOUT; it now uses the
  defaults.

Co-authored-by: Claude Opus 5.5 <[email protected]>
hawkli-1994 pushed a commit to rongxinzy/deer-flow that referenced this pull request Sep 27, 2026
…ance#5801)

os.getenv's default only covers an unset variable, so `docker run -e
GATEWAY_PORT=` — or `GATEWAY_PORT=${PORT}` with PORT unset — reached
int("") and aborted the create_app() import that both entry points run.
Blank now falls back to the declared default, while "0" stays a real port.
hawkli-1994 pushed a commit to rongxinzy/deer-flow that referenced this pull request Sep 27, 2026
…out as unset (bytedance#5840)

Follows bytedance#5801 for the remaining numeric env reads that break on an empty
value (`-e VAR=`, or `${VAR}` with VAR unset):

- The WeChat QR worker guard read a blank GATEWAY_WORKERS as an invalid
  count and refused QR login, although the compose command starts one
  worker for it (`--workers ${GATEWAY_WORKERS:-1}`) and the other worker
  checks treat it as 1. A blank value now falls through to WEB_CONCURRENCY
  and then 1, so the guard still fails closed for real multi-worker setups.
- The Lark broker aborted startup on int("") for a blank
  DEERFLOW_LARK_BROKER_PORT or DEERFLOW_LARK_BROKER_TIMEOUT; it now uses the
  defaults.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:backend Gateway / runtime / core backend under backend/ needs-validation Touches front/back contract surface; needs real-path validation risk:high High risk: backend API, agents, sandbox, auth, deps, CI size/S PR changes 20-100 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Blank GATEWAY_PORT/GATEWAY_HOST aborts the gateway at import (ValueError: invalid literal for int())

2 participants