Repository navigation
[4.0.0-beta desktop] Standalone app prints port 8888 but Flask binds 8080 #249
Description
PROBLEM
Double-clicking the .app / .exe (no PORT env var) produces two different ports:
| Code path | Port |
|---|---|
app_base.py: PORT = int(os.environ.get('PORT', 8888)) |
8888 at import |
Startup banner in app.py: Open http://localhost:{PORT} |
prints 8888 |
app.py get_server_port(default=8080) + config.json server.port |
binds 8080 |
__main__ webbrowser.open(...) |
8080 |
| GitHub Release notes | 8080 |
| README "local Python" | 8888 |
| README "Docker" | 5000 (compose actually maps 8080) |
Symptom: console says open :8888 (connection refused). The auto-opened browser may hit :8080. Users think the app is broken.
Existing unit test encodes the bug:
tests/python/test_app_flask.py::TestServerConfiguration.test_port_default_is_8888
Suggested RCA
Port was never unified after the CBL migration.
app_base.pykept the old AirPlay-avoidance default of 8888 (comment even says "5000 is used by AirPlay").app.py/config.default.json/ Docker / compose moved to 8080.- The import-time banner uses
app_base.PORTbeforeget_server_port()runs. _startup_banner/ print happen at import (gunicorn + desktop), not at bind time.
Suggested FIX
One constant, one resolver, print the port you actually bind.
# app.py (or a tiny app/ports.py so app_base can import it without a cycle)
DEFAULT_PORT = 8080
def get_server_port(default: int = DEFAULT_PORT) -> int:
env = os.environ.get("PORT")
if env:
try:
return int(env)
except ValueError:
logger.warning("ignoring invalid PORT env var=%s", env)
cfg = _load_server_config().get("server") or {}
val = cfg.get("port")
if isinstance(val, int) and 0 < val < 65536:
return val
if isinstance(val, str) and val.isdigit():
return int(val)
return default# app_base.py — stop inventing a second default
from app import get_server_port, DEFAULT_PORT # or move helper to ports.py
PORT = get_server_port()Better (avoids import cycle): put DEFAULT_PORT + get_server_port in app/ports.py. Both app.py and app_base.py import from there.
Print after resolution:
PORT = get_server_port()
print(f"🌐 Open http://localhost:{PORT} in your browser")Update README / AGENT.md / packaging docs / Release body: Docker and desktop are 8080. Delete the "Docker uses 5000" sentence.
Unit tests
Replace the change-detector that asserts 8888.
# tests/python/test_ports.py
import os
from unittest.mock import patch
from ports import DEFAULT_PORT, get_server_port
def test_default_port_is_8080():
assert DEFAULT_PORT == 8080
def test_env_port_wins(monkeypatch):
monkeypatch.setenv("PORT", "5555")
assert get_server_port() == 5555
def test_invalid_env_port_falls_back(monkeypatch):
monkeypatch.setenv("PORT", "not-a-port")
with patch("ports._load_server_config", return_value={}):
assert get_server_port() == DEFAULT_PORT
def test_config_json_port_used_when_no_env(monkeypatch):
monkeypatch.delenv("PORT", raising=False)
with patch("ports._load_server_config", return_value={"server": {"port": 9090}}):
assert get_server_port() == 9090
def test_banner_port_matches_bind_port(capsys, monkeypatch):
monkeypatch.setenv("PORT", "8080")
import app
# After fix, the printed URL must contain the resolved port, not 8888.
# Assert via a small _startup_banner(port) helper:
app._startup_banner(8080)
out = capsys.readouterr().out
assert "localhost:8080" in out
assert "localhost:8888" not in outDelete or invert test_port_default_is_8888.
Context: found on branch release-4.0.0-beta while reviewing standalone macOS .app / Windows .exe packaging. Issues in this batch share the title prefix [4.0.0-beta desktop].
Activity
Fujio-Turner commentedon Sep 15, 2026
Fixed on
release-4.0.0-betavia #263