Skip to content

fix(server): crash on startup when PORT is 10000 or higher - #128

Open
YAMRAJ13y wants to merge 1 commit into
calesthio:masterfrom
YAMRAJ13y:fix/startup-banner-5-digit-port
Open

fix(server): crash on startup when PORT is 10000 or higher#128
YAMRAJ13y wants to merge 1 commit into
calesthio:masterfrom
YAMRAJ13y:fix/startup-banner-5-digit-port

Conversation

@YAMRAJ13y

Copy link
Copy Markdown

Summary

node server.mjs crashes before the server binds whenever PORT is 10000 or higher. The startup banner is rebuilt so its frame width is derived from its contents instead of hand-counted padding.

Why

The banner padded each row with String.repeat() arithmetic that assumed a 4-digit port. For any port in the 10000–65535 range the health-check row evaluates to ' '.repeat(-1), which throws:

$ PORT=10000 node server.mjs
[Crucix] FATAL — Server failed to start:
  RangeError: Invalid count value: -1
    at String.repeat (<anonymous>)
    at start (server.mjs:417:59)

This happens before app.listen(), so the server never binds and the dashboard never comes up. Two things make it hard to diagnose:

  • the error message never mentions the port, so it doesn't look port-related
  • under restart: unless-stopped in docker-compose.yml it becomes a silent crash loop

PORT is documented as a free-form override in both README.md and .env.example, and docker-compose.yml maps ${PORT:-3117}, so 10000+ is a supported configuration today.

While fixing it I found the same hand-counted padding was already producing wrong output at the default port — the right border was misaligned by 1–2 columns on five of the six rows:

  ║  Dashboard:  http://localhost:3117          ║
  ║  Health:     http://localhost:3117/api/health║
  ║  Refresh:    Every 15 min                  ║
  ║  LLM:        disabled                       ║

A minimal Math.max(0, …) guard would stop the crash but truncate the health URL to http://localhost:10000/api/healt for 5-digit ports, so renderBanner() computes the width from the longest row instead. Output is unchanged at the default port apart from the border now lining up:

  ╔═══════════════════════════════════════════════╗
  ║          CRUCIX INTELLIGENCE ENGINE           ║
  ║          Local Palantir · 26 Sources          ║
  ╠═══════════════════════════════════════════════╣
  ║  Dashboard:  http://localhost:65535           ║
  ║  Health:     http://localhost:65535/api/health║
  ║  Refresh:    Every 15 min                     ║
  ║  LLM:        disabled                         ║
  ║  Telegram:   disabled                         ║
  ║  Discord:    disabled                         ║
  ╚═══════════════════════════════════════════════╝

Scope

  • Focused bug fix
  • Small UX improvement
  • New source
  • Dashboard change
  • Docs/config change

Validation

# reproduces on master, fixed on this branch
PORT=10000 node server.mjs

# boots and renders aligned at every boundary
for p in 80 3117 10000 65535; do PORT=$p node server.mjs; done

# widest-content case: long provider name + 3-digit interval + webhook row
PORT=65535 LLM_PROVIDER=openrouter REFRESH_INTERVAL_MINUTES=120 \
  DISCORD_WEBHOOK_URL=... node server.mjs

node --check server.mjs
node --test test/*.test.mjs     # 45 pass, 1 skipped, 0 fail — unchanged

Config and Docs

  • No new environment variables
  • .env.example updated if needed — not needed, PORT already documented
  • README.md updated if behavior changed — not needed, this restores documented behavior

Notes

The banner still reads 26 Sources while apis/briefing.mjs wires 29 and README.md says 27. That mismatch is already covered by #124 and #84, so I left it alone to keep this diff to one bug.

The startup banner padded each row with hand-counted `String.repeat()`
arithmetic that assumed a 4-digit port. For any port in the 10000-65535
range the health-check row evaluated to `' '.repeat(-1)`, which throws:

    $ PORT=10000 node server.mjs
    [Crucix] FATAL — Server failed to start:
      RangeError: Invalid count value: -1
        at String.repeat (<anonymous>)
        at start (server.mjs:417:59)

This happens before `app.listen()`, so the server never binds. The error
does not mention the port, and under the `restart: unless-stopped` policy
in docker-compose.yml it becomes a silent crash loop. README.md and
.env.example both document PORT as a free-form override.

Replace the hand-counted padding with a `renderBanner()` helper that
derives the frame width from its own contents. This also fixes two
existing cosmetic bugs: the box was already misaligned by 1-2 columns on
five of six rows at the default port, and a naive minimal fix would have
truncated the health URL to `/api/healt` for 5-digit ports.

Validated with PORT=80, 3117, 10000 and 65535, and with a longer provider
name and 3-digit refresh interval. Existing test suite unaffected
(45 pass, 1 skipped).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@YAMRAJ13y
YAMRAJ13y requested a review from calesthio as a code owner August 9, 2026 14:10
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