Skip to content

fix(main): support EACCES in server port fallback - #541

Merged
khaira777 merged 4 commits into
mainfrom
fix/windows-docker-eacces-port-fallback
Aug 28, 2026
Merged

fix(main): support EACCES in server port fallback#541
khaira777 merged 4 commits into
mainfrom
fix/windows-docker-eacces-port-fallback

Conversation

@khaira777

Copy link
Copy Markdown
Contributor

What Changed

  • Added EACCES error handling alongside EADDRINUSE in the port collision retry loop across the API server, KDS server, and Server App.
  • Standardized server startup across all three servers using a recursive tryListen pattern with one-time event listeners to prevent duplicate listener accumulation during retries.
  • Expanded tests/server-port-collision.test.ts with comprehensive coverage for ephemeral ports, EADDRINUSE collisions, EACCES errors, and retry exhaustion across all servers.

Risk Assessment

✅ Low: The changes cleanly unify port fallback handling across servers and support EACCES on Windows/Docker with appropriate regression tests.

Testing

Successfully executed targeted automated tests covering ephemeral ports, EADDRINUSE collisions, EACCES permission fallback, and 10-attempt retry exhaustion across main/server.ts, main/kds-server.ts, and main/server-app.ts, verifying full health check and websocket readiness on fallback ports.

Evidence: Server Port Collision and EACCES Fallback Test Output

[Test] Testing API server... [Server] Frontend build not found. Run npm run build:frontend first. [Server] HTTP server running on http://localhost:60530
[KDS] WebSocket server setup complete [Server] KDS WebSocket running on ws://localhost:60530/kds [Server] HTTP/WebSocket server stopped ✅ API Server ephemeral port passed [Server] Frontend build not found. Run npm run build:frontend first. [Server] Port 60532 in use (EADDRINUSE), trying 60533 [Server] HTTP server running on http://localhost:60533
[KDS] WebSocket server setup complete [Server] KDS WebSocket running on ws://localhost:60533/kds [Server] HTTP/WebSocket server stopped ✅ API Server EADDRINUSE collision fallback passed [Server] Frontend build not found. Run npm run build:frontend first. [Server] Port 34500 in use (EACCES), trying 34501 [Server] HTTP server running on http://localhost:34501
[KDS] WebSocket server setup complete [Server] KDS WebSocket running on ws://localhost:34501/kds [Server] HTTP/WebSocket server stopped ✅ API Server EACCES fallback passed [Server] Frontend build not found. Run npm run build:frontend first. [Server] Port 35000 in use (EACCES), trying 35001 [Server] Port 35001 in use (EACCES), trying 35002 [Server] Port 35002 in use (EACCES), trying 35003 [Server] Port 35003 in use (EACCES), trying 35004 [Server] Port 35004 in use (EACCES), trying 35005 [Server] Port 35005 in use (EACCES), trying 35006 [Server] Port 35006 in use (EACCES), trying 35007 [Server] Port 35007 in use (EACCES), trying 35008 [Server] Port 35008 in use (EACCES), trying 35009 [Server] Failed to bind to any port after 10 attempts starting from 35000 [Server] HTTP/WebSocket server stopped ✅ API Server retry exhaustion passed [Test] Testing KDS server... [KDS Server] Static build not found. Run npm run build:frontend first. [KDS Server] Port 60536 in use (EADDRINUSE), trying 60537 [KDS Server] HTTP server running on http://localhost:60537
[KDS] WebSocket server setup complete [KDS Server] WebSocket running on ws://localhost:60537/kds [KDS Server] HTTP/WebSocket server stopped ✅ KDS Server EADDRINUSE collision fallback passed [KDS Server] Static build not found. Run npm run build:frontend first. [KDS Server] Port 36500 in use (EACCES), trying 36501 [KDS Server] HTTP server running on http://localhost:36501
[KDS] WebSocket server setup complete [KDS Server] WebSocket running on ws://localhost:36501/kds [KDS Server] HTTP/WebSocket server stopped ✅ KDS Server EACCES fallback passed [KDS Server] Static build not found. Run npm run build:frontend first. [KDS Server] Port 37000 in use (EACCES), trying 37001 [KDS Server] Port 37001 in use (EACCES), trying 37002 [KDS Server] Port 37002 in use (EACCES), trying 37003 [KDS Server] Port 37003 in use (EACCES), trying 37004 [KDS Server] Port 37004 in use (EACCES), trying 37005 [KDS Server] Port 37005 in use (EACCES), trying 37006 [KDS Server] Port 37006 in use (EACCES), trying 37007 [KDS Server] Port 37007 in use (EACCES), trying 37008 [KDS Server] Port 37008 in use (EACCES), trying 37009 [KDS Server] Failed to bind to any port after 10 attempts starting from 37000 [KDS Server] HTTP/WebSocket server stopped ✅ KDS Server retry exhaustion passed [Test] Testing Server App... [Server App] Static build not found. Run npm run build:frontend first. [Server App] Port 60537 in use (EADDRINUSE), trying 60538 [Server App] HTTP server running on http://localhost:60538
[Server App] HTTP server stopped ✅ Server App EADDRINUSE collision fallback passed [Server App] Static build not found. Run npm run build:frontend first. [Server App] Port 38500 in use (EACCES), trying 38501 [Server App] HTTP server running on http://localhost:38501
[Server App] HTTP server stopped ✅ Server App EACCES fallback passed [Server App] Static build not found. Run npm run build:frontend first. [Server App] Port 39000 in use (EACCES), trying 39001 [Server App] Port 39001 in use (EACCES), trying 39002 [Server App] Port 39002 in use (EACCES), trying 39003 [Server App] Port 39003 in use (EACCES), trying 39004 [Server App] Port 39004 in use (EACCES), trying 39005 [Server App] Port 39005 in use (EACCES), trying 39006 [Server App] Port 39006 in use (EACCES), trying 39007 [Server App] Port 39007 in use (EACCES), trying 39008 [Server App] Port 39008 in use (EACCES), trying 39009 [Server App] Failed to bind to any port after 10 attempts starting from 39000 [Server App] HTTP server stopped ✅ Server App retry exhaustion passed 🎉 ALL PORT COLLISION & EACCES FALLBACK TESTS PASSED!


> flo-desktop@3.3.1-beta.3 test:server-port-collision
> node tests/run-electron-node-test.cjs tests/server-port-collision.test.ts

[DB] Opening database at: /var/folders/y_/1ltcxtwj0zd_w1dg9jv4jl580000gn/T/flo-port-collision-eUBWK6/flo.db
[DB] Schema: v0 → v75
[DB] Triggering auto-backup before migrating v0 → v75...
[DB] Auto-backup before migrating v0 → v75 created at /var/folders/y_/1ltcxtwj0zd_w1dg9jv4jl580000gn/T/flo-port-collision-eUBWK6/backups/flo-backup-2026-08-28T22-03-05-382Z-pre-v0-to-v75.db
[DB] Applying migration v1: initial_schema
[DB] Install defaults loaded; first-run setup pending
[DB] Migration v1 complete
[DB] Applying migration v2: hash_plaintext_pins
[DB] Migration v2 complete
[DB] Applying migration v3: cloud_identity_and_outbox
[DB] Migration v3 complete
[DB] Applying migration v4: add_notes_limits_settings
[DB] Migration v4 complete
[DB] Applying migration v5: add_print_logs_table
[DB] Migration v5 complete
[DB] Applying migration v6: add_loyalty_settings
[DB] Migration v6 complete
[DB] Applying migration v7: add_discount_settings
[DB] Migration v7 complete
[DB] Applying migration v8: add_loyalty_index
[DB] Migration v8 complete
[DB] Applying migration v9: add_sequences_table
[DB] Migration v9 complete
[DB] Applying migration v10: fix_sequences_composite_key
[DB] Migration v10 complete
[DB] Applying migration v11: first_run_setup_uses_welcome_form
[DB] Migration v11 complete
[DB] Applying migration v12: fix_table_integer_ids
[DB] Migration v12 complete
[DB] Applying migration v13: fix_null_table_ids
[DB] Migration v13 complete
[DB] Applying migration v14: simplify_loyalty_settings
[DB] Migration v14 complete
[DB] Applying migration v15: add_instagram_handle_setting
[DB] Migration v15 complete
[DB] Applying migration v16: add_terms_accepted_at_to_users
[DB] Migration v16 complete
[DB] Applying migration v17: add_held_orders_table
[DB] Migration v17 complete
[DB] Applying migration v18: fix_null_category_ids
[DB] Migration v18 complete
[DB] Applying migration v19: backfill_product_cb_percent_and_tags
[DB] Migration v19 complete
[DB] Applying migration v20: add_tables_is_active
[DB] Migration v20 complete
[DB] Applying migration v21: clear_legacy_loyalty_expiry
[DB] Migration v21 complete
[DB] Applying migration v22: add_customers_phone_digits
[DB] Migration v22 complete
[DB] Applying migration v23: normalize_customer_phones
[MIGRATION v23] normalized: 0, unparseable: 0
[MIGRATION v23] merged 0 duplicate customer(s)
[MIGRATION v23] verification: 0 customers, 0 still non-E.164
[DB] Migration v23 complete
[DB] Applying migration v24: normalize_customer_phones_retry
[MIGRATION v24] normalized: 0, unparseable: 0
[DB] Migration v24 complete
[DB] Applying migration v25: add_order_item_addons_table
[MIGRATION v25] backfilled addons for 0 order items (0 unparseable, skipped)
[DB] Migration v25 complete
[DB] Applying migration v26: add_kds_default_view
[DB] Migration v26 complete
[DB] Applying migration v27: add_station_printer_link_and_user_stations
[DB] Migration v27 complete
[DB] Applying migration v28: seed_telemetry_settings
[DB] Migration v28 complete
[DB] Applying migration v29: whatsapp_messaging
[DB] Migration v29 complete
[DB] Applying migration v30: drop_order_items_addons_json_column
[MIGRATION v30] backfilled 0 order_item(s) still missing a normalized addons snapshot
[MIGRATION v30] Dropped order_items.addons — order_item_addons is now the only place selected addons live.
[DB] Migration v30 complete
[DB] Applying migration v31: add_customers_tag_counts_column
[DB] Migration v31 complete
[DB] Applying migration v32: add_kds_and_kot_printing_toggles
[DB] Migration v32 complete
[DB] Applying migration v33: add_addon_groups_allow_multiple_quantities
[DB] Migration v33 complete
[DB] Applying migration v34: add_order_items_voided_at
[DB] Migration v34 complete
[DB] Applying migration v35: add_tax_pack_configuration_tables
[DB] Migration v35 complete
[DB] Applying migration v36: add_product_and_addon_tax_categories
[DB] Migration v36 complete
[DB] Applying migration v37: add_transaction_tax_snapshots_and_charge_categories
[DB] Migration v37 complete
[DB] Applying migration v38: register_bundled_tax_pack_versions
[DB] Migration v38 complete
[DB] Applying migration v39: add_users_tokens_valid_after
[DB] Migration v39 complete
[DB] Applying migration v40: v2_cloud_defaults_and_tax_toggle
[DB] Migration v40 complete
[DB] Applying migration v41: support_ticket_outbox
[DB] Migration v41 complete
[DB] Applying migration v42: add_global_cashback_percent
[DB] Migration v42 complete
[DB] Applying migration v43: telemetry_default_on_for_new_installs
[DB] Migration v43 complete
[DB] Applying migration v44: store_diagnostics_outbox
[DB] Migration v44 complete
[DB] Applying migration v45: add_performance_indexes_and_normalize_timestamps
[DB] Migration v45 complete
[DB] Applying migration v46: normalize_cloud_enabled_flags_to_01
[DB] Migration v46 complete
[DB] Applying migration v47: store_diagnostics_enabled_by_default
[DB] Migration v47 complete
[DB] Applying migration v48: deactivate_reusable_demo_credentials
[DB] Migration v48 complete
[DB] Applying migration v49: add_payment_idempotency_records
[DB] Migration v49 complete
[DB] Applying migration v50: add_order_idempotency_records
[DB] Migration v50 complete
[DB] Applying migration v51: enforce_global_payment_transaction_refs
[DB] Migration v51 complete
[DB] Applying migration v52: restore_method_scoped_transaction_refs
[DB] Migration v52 complete
[DB] Applying migration v53: scope_idempotency_records_to_user
[DB] Migration v53 complete
[DB] Applying migration v54: repair_retry_ownership_and_payment_reference_history
[DB] Migration v54 complete
[DB] Applying migration v55: durable_token_revocations
[DB] Migration v55 complete
[DB] Applying migration v56: persist_station_assignment_scope
[DB] Migration v56 complete
[DB] Applying migration v57: rename_gstin_to_generic_tax_registration_number
[DB] Migration v57 complete
[DB] Applying migration v58: configurable_manual_payment_methods
[DB] Migration v58 complete
[DB] Applying migration v59: split_checks_by_item_quantity
[DB] Migration v59 complete
[DB] Applying migration v60: seed_split_checks_disabled_setting
[DB] Migration v60 complete
[DB] Applying migration v61: seed_server_app_enabled_setting
[DB] Migration v61 complete
[DB] Applying migration v62: normalize_cloud_last_error
[DB] Migration v62 complete
[DB] Applying migration v63: seed_printer_trim_decimals_setting
[DB] Migration v63 complete
[DB] Applying migration v64: drop_unused_printer_usb_device_path
[DB] Migration v64 complete
[DB] Applying migration v65: seed_bill_content_visibility_settings
[DB] Migration v65 complete
[DB] Applying migration v66: seed_bill_template_settings
[DB] Migration v66 complete
[DB] Applying migration v67: persist_order_item_inventory_deductions
[DB] Migration v67 complete
[DB] Applying migration v68: add_invoice_numbering_settings
[DB] Migration v68 complete
[DB] Applying migration v69: installed_plugin_print_templates
[DB] Migration v69 complete
[DB] Applying migration v70: rename_waiter_role_to_server
[DB] Migration v70 complete
[DB] Applying migration v71: repair_durable_token_revocations
[DB] Migration v71 complete
[DB] Applying migration v72: merchant_print_templates
[DB] Migration v72 complete
[DB] Applying migration v73: normalize_merchant_template_payloads
[MIGRATION v73] normalized 0 merchant template payload(s); 0 already canonical or left untouched
[DB] Migration v73 complete
[DB] Applying migration v74: add_country_packs_disclaimer_ack
[DB] Migration v74 complete
[DB] Applying migration v75: add_printer_cash_drawer_pulse
[DB] Migration v75 complete
[DB] integrity_check: ok
[DB] foreign_key_check: clean
[Test] Testing API server...
[Server] Frontend build not found. Run `npm run build:frontend` first.
[Server] HTTP server running on http://localhost:60526
[KDS] WebSocket server setup complete
[Server] KDS WebSocket running on ws://localhost:60526/kds
[Server] HTTP/WebSocket server stopped
✅ API Server ephemeral port passed
[Server] Frontend build not found. Run `npm run build:frontend` first.
[Server] Port 60528 in use (EADDRINUSE), trying 60529
[Server] HTTP server running on http://localhost:60529
[KDS] WebSocket server setup complete
[Server] KDS WebSocket running on ws://localhost:60529/kds
[Server] HTTP/WebSocket server stopped
✅ API Server EADDRINUSE collision fallback passed
[Server] Frontend build not found. Run `npm run build:frontend` first.
[Server] Port 34500 in use (EACCES), trying 34501
[Server] HTTP server running on http://localhost:34501
[KDS] WebSocket server setup complete
[Server] KDS WebSocket running on ws://localhost:34501/kds
[Server] HTTP/WebSocket server stopped
✅ API Server EACCES fallback passed
[Server] Frontend build not found. Run `npm run build:frontend` first.
[Server] Port 35000 in use (EACCES), trying 35001
[Server] Port 35001 in use (EACCES), trying 35002
[Server] Port 35002 in use (EACCES), trying 35003
[Server] Port 35003 in use (EACCES), trying 35004
[Server] Port 35004 in use (EACCES), trying 35005
[Server] Port 35005 in use (EACCES), trying 35006
[Server] Port 35006 in use (EACCES), trying 35007
[Server] Port 35007 in use (EACCES), trying 35008
[Server] Port 35008 in use (EACCES), trying 35009
[Server] Failed to bind to any port after 10 attempts starting from 35000
[Server] HTTP/WebSocket server stopped
✅ API Server retry exhaustion passed
[Test] Testing KDS server...
[KDS Server] Static build not found. Run `npm run build:frontend` first.
[KDS Server] Port 60532 in use (EADDRINUSE), trying 60533
[KDS Server] HTTP server running on http://localhost:60533
[KDS] WebSocket server setup complete
[KDS Server] WebSocket running on ws://localhost:60533/kds
[KDS Server] HTTP/WebSocket server stopped
✅ KDS Server EADDRINUSE collision fallback passed
[KDS Server] Static build not found. Run `npm run build:frontend` first.
[KDS Server] Port 36500 in use (EACCES), trying 36501
[KDS Server] HTTP server running on http://localhost:36501
[KDS] WebSocket server setup complete
[KDS Server] WebSocket running on ws://localhost:36501/kds
[KDS Server] HTTP/WebSocket server stopped
✅ KDS Server EACCES fallback passed
[KDS Server] Static build not found. Run `npm run build:frontend` first.
[KDS Server] Port 37000 in use (EACCES), trying 37001
[KDS Server] Port 37001 in use (EACCES), trying 37002
[KDS Server] Port 37002 in use (EACCES), trying 37003
[KDS Server] Port 37003 in use (EACCES), trying 37004
[KDS Server] Port 37004 in use (EACCES), trying 37005
[KDS Server] Port 37005 in use (EACCES), trying 37006
[KDS Server] Port 37006 in use (EACCES), trying 37007
[KDS Server] Port 37007 in use (EACCES), trying 37008
[KDS Server] Port 37008 in use (EACCES), trying 37009
[KDS Server] Failed to bind to any port after 10 attempts starting from 37000
[KDS Server] HTTP/WebSocket server stopped
✅ KDS Server retry exhaustion passed
[Test] Testing Server App...
[Server App] Static build not found. Run `npm run build:frontend` first.
[Server App] Port 60533 in use (EADDRINUSE), trying 60534
[Server App] HTTP server running on http://localhost:60534
[Server App] HTTP server stopped
✅ Server App EADDRINUSE collision fallback passed
[Server App] Static build not found. Run `npm run build:frontend` first.
[Server App] Port 38500 in use (EACCES), trying 38501
[Server App] HTTP server running on http://localhost:38501
[Server App] HTTP server stopped
✅ Server App EACCES fallback passed
[Server App] Static build not found. Run `npm run build:frontend` first.
[Server App] Port 39000 in use (EACCES), trying 39001
[Server App] Port 39001 in use (EACCES), trying 39002
[Server App] Port 39002 in use (EACCES), trying 39003
[Server App] Port 39003 in use (EACCES), trying 39004
[Server App] Port 39004 in use (EACCES), trying 39005
[Server App] Port 39005 in use (EACCES), trying 39006
[Server App] Port 39006 in use (EACCES), trying 39007
[Server App] Port 39007 in use (EACCES), trying 39008
[Server App] Port 39008 in use (EACCES), trying 39009
[Server App] Failed to bind to any port after 10 attempts starting from 39000
[Server App] HTTP server stopped
✅ Server App retry exhaustion passed

🎉 ALL PORT COLLISION & EACCES FALLBACK TESTS PASSED!
[DB] Database closed

Pipeline

Updates from git push no-mistakes

⏭️ **intent** - skipped

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • npm run test:server-port-collision
  • npm run test:shutdown-lifecycle
  • npm run test:kds-integration
  • npm run test:smoke
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown

Greptile Summary

The PR standardizes API, KDS, and Server App startup around a reusable tryListen loop that retries both EADDRINUSE and EACCES failures.

  • Reuses one HTTP server per startup while managing retry listeners explicitly.
  • Resolves and reports the actual bound port after fallback.
  • Adds coverage for ephemeral ports, collisions, permission errors, and retry exhaustion across all three servers.

Confidence Score: 5/5

The PR appears safe to merge with no concrete blocking or independently actionable non-blocking issues identified.

The bounded retry implementations consistently remove paired listeners, preserve successful startup initialization, expose the actual bound ports, and reject after exhaustion across all three servers.

Important Files Changed

Filename Overview
main/server.ts Refactors API server binding into a one-time-listener retry loop supporting both address collisions and permission failures.
main/kds-server.ts Applies the same bounded port fallback and listener lifecycle to the standalone KDS HTTP/WebSocket server.
main/server-app.ts Extends the existing Server App retry loop to handle EACCES and report its runtime base port consistently.
tests/server-port-collision.test.ts Expands isolated integration coverage across successful fallback, actual port reporting, health readiness, and retry exhaustion.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Create HTTP server] --> B[Listen on current port]
    B -->|listening| C[Record active port]
    C --> D[Initialize WebSocket services]
    D --> E[Resolve startup]
    B -->|EADDRINUSE or EACCES| F{10 attempts reached?}
    F -->|No| G[Increment port]
    G --> B
    F -->|Yes| H[Reject startup]
    B -->|Other error| H
Loading

Reviews (1): Last reviewed commit: "no-mistakes(document): Verify documentat..." | Re-trigger Greptile

@khaira777
khaira777 merged commit 6e87ebc into main Aug 28, 2026
15 checks passed
@khaira777
khaira777 deleted the fix/windows-docker-eacces-port-fallback branch August 28, 2026 22:09
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.

1 participant