Skip to content

Navigation Menu

Sign in
Sign up

fix(webserver): refuse to boot when a fixed port is already bound - #799

Open
ChampionDesigns wants to merge 1 commit into
decentespresso:main from
ChampionDesigns:ben/webserver-port-conflict
Open

fix(webserver): refuse to boot when a fixed port is already bound #799
ChampionDesigns wants to merge 1 commit into
decentespresso:main from
ChampionDesigns:ben/webserver-port-conflict

Conversation

@ChampionDesigns

@ChampionDesigns ChampionDesigns commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

The app could boot with no API at all and say nothing.

startWebServer binds two fixed ports: 8080 for the REST and WebSocket API, and 4001 for the API
docs. main() wrapped the call in a bare catch that logged "failed to start web server" and
carried on.

That failure is close to invisible. The WebUI binds an ephemeral port and is unaffected, so the
skin still loads and the app looks normal — only every REST call and every WebSocket behind it is
gone. BootTiming.mark('webserver_up') fires either way. The user sees an app that opened
correctly and then misbehaves, with the reason only in the log.

Two Decaid-family apps installed on one device share those fixed ports, so the common cause is
simply that the other one is already running.

Base: main. Independent.

Design notes

  • serveOrReportPortInUse raises a typed WebServerPortInUse for EADDRINUSE and rethrows
    every other failure unchanged
    , so a permission denial keeps its own error path and is never
    reported to the user as a port clash.
  • isAddressInUse matches the OS code per platform — 98 on Linux and Android, 48 on macOS and
    iOS, 10048 on Windows — because Dart reports the raw code and does not normalise it.
  • main() stops on WebServerPortInUse and shows a screen naming the port, offering "Check again"
    and "Close this app".

Linked Issue

N/A

Verification

  • flutter analyze — clean.
  • flutter testfull suite 3904 passed against current main, including
    port_binding_test and webserver_port_conflict_app_test.
  • dart format — clean on every changed file.
  • Verified on hardware. This change ships in the Decaid-Canary build Ben runs on his own
    machine, and has been exercised in normal use rather than only under test.

Impact

  • User-visible: the app now refuses to boot and says which port is taken, instead of running
    without an API. That is a deliberate behaviour change.
  • Compatibility: none for a normal single-app install; the path is only reached when a bind
    fails.
  • API: none.
  • Security: none. A permission failure is not swallowed as a port clash.

Contributor Responsibility

AI-assisted development is allowed. The submitter remains responsible for the submitted work.

  • I have reviewed and understand all changes in this PR and take responsibility for their correctness, security, behavior, licensing, and provenance, including any AI-assisted or AI-generated work.

startWebServer binds two fixed ports: 8080 for the REST and WebSocket API, and
4001 for the API docs. main() wrapped the call in a bare catch that logged
"failed to start web server" and carried on, so a bind failure left the app
running with no API at all.
That failure is close to invisible. The WebUI binds an ephemeral port and is
unaffected, so the skin still loads and the app looks normal; only every REST
call and every WebSocket behind it is gone. BootTiming.mark('webserver_up')
fires either way. The user sees an app that opened correctly and then
misbehaves, with the reason only in the log.
Two Decaid-family apps installed on one device share those fixed ports, so the
common cause is that the other one is already running.
- serveOrReportPortInUse raises a typed WebServerPortInUse for EADDRINUSE and
 rethrows every other failure unchanged, so a permission denial keeps its own
 error path and is never reported to the user as a port clash.
- isAddressInUse matches the OS code per platform -- 98 on Linux and Android,
 48 on macOS and iOS, 10048 on Windows -- because Dart reports the raw code
 and does not normalise it.
- main() stops on WebServerPortInUse and shows a screen naming the port instead
 of booting without an API.
The screen offers "Check again", which re-probes the port, and "Close this app".
It does not try to close the other app: Android does not let one app stop
another, and killBackgroundProcesses needs its own permission and only ever
touches background processes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /