Skip to content

fix(editor): use a literal dev port so bun dev works on Windows - #551

Open
evolv3ai wants to merge 2 commits into
pascalorg:mainfrom
evolv3ai:fix/windows-dev-port-expansion
Open

fix(editor): use a literal dev port so bun dev works on Windows#551
evolv3ai wants to merge 2 commits into
pascalorg:mainfrom
evolv3ai:fix/windows-dev-port-expansion

Conversation

@evolv3ai

@evolv3ai evolv3ai commented Jul 27, 2026

Copy link
Copy Markdown

Problem

bun dev fails immediately on Windows. The editor dev task dies before Next.js starts:

editor:dev: $ dotenv -e ../../.env.local -- next dev --port ${PORT:-3002}
editor:dev: error: option '-p, --port <port>' argument '${PORT:-3002}' is invalid. '${PORT:-3002}' is not a non-negative number.
editor:dev: error: script "dev" exited with code 1

Cause

On Windows, bun run executes package scripts with Bun's own built-in shell rather than handing them to a POSIX shell. That shell does not implement default-value parameter expansion, so ${PORT:-3002} in apps/editor/package.json reaches Next.js verbatim and is rejected.

Setting PORT in the environment first does not work around it — the literal is never expanded either way. There is no shell-level workaround from the caller's side; the script itself has to avoid the syntax.

Fix

Use the documented default port directly:

-"dev": "dotenv -e ../../.env.local -- next dev --port ${PORT:-3002}"
+"dev": "dotenv -e ../../.env.local -- next dev --port 3002"

Verified on Windows 11 / Bun 1.3.6 / Node 24.18.0 — bun dev now boots the whole workspace and the editor serves HTTP 200 on http://localhost:3002.

Tradeoff, and an alternative if you'd prefer it

This drops the PORT override that SETUP.md documents. I kept the change to one line because that's the smallest thing that unblocks Windows, but I'm happy to switch to a small cross-platform launcher instead, which would preserve PORT on every platform:

// apps/editor/scripts/dev.mjs
import { spawn } from "node:child_process";
const port = process.env.PORT ?? "3002";
spawn("next", ["dev", "--port", port], { stdio: "inherit", shell: true });

Just say which you'd rather have and I'll update the PR.

Two unrelated things I noticed while debugging

Not touched in this PR, but worth flagging:

  1. The root dev script is also a no-op on Windows. It begins with set -a && . ./.env 2>/dev/null; set +a; turbo run dev --env-mode=loose. Bun's shell has no set builtin, so it prints bun: command not found: set twice and silently skips loading .env. Turbo still runs, so it's non-fatal, but any variables in a root .env are never loaded on Windows.

  2. .env.example and SETUP.md disagree on the default port. .env.example says # Dev server port (default: 3000) / # PORT=3000, while SETUP.md and the dev script both use 3002.


Note

Low Risk
Routing and local dev ergonomics only; no auth, data, or production runtime behavior beyond serving the correct editor path.

Overview
MCP and hosted links use /editor/<id>, but this app’s page lives at /scene/[id], so those URLs 404 locally. A Next.js rewrite maps /editor/:id/scene/:id without changing the browser path, which matters because client code (e.g. scan upload in view-toggles.tsx) parses the project id from /editor/....

The dev script now runs next dev on port 3002 with a literal port (no ${PORT:-3002}) so bun dev works on Windows, and adds --hostname 0.0.0.0 so the dev server is reachable beyond localhost.

Reviewed by Cursor Bugbot for commit 76add12. Bugbot is set up for automated code reviews on this repo. Configure here.

evolv3ai and others added 2 commits July 27, 2026 12:30
On Windows, `bun run` executes package scripts with Bun's own built-in
shell rather than a POSIX shell. That shell does not implement
default-value parameter expansion, so `${PORT:-3002}` is passed through
to Next.js verbatim and the dev server exits immediately:

    error: option '-p, --port <port>' argument '${PORT:-3002}' is invalid.
           '${PORT:-3002}' is not a non-negative number.

Setting PORT in the environment does not help — the literal is never
expanded either way.

Replace the expansion with the documented default port (3002).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The scene store hands out `/editor/<id>` as a project's canonical URL
(`editorUrlForScene` in packages/mcp/src/storage/sqlite-scene-store.ts)
and `/api/scenes` reports it verbatim, but this app only routes
`/scene/[id]`. Every MCP-reported `editorUrl` therefore 404s.

Rewrite `/editor/:id` to `/scene/:id` rather than redirect: client code
parses the project id back out of the browser path (scan upload in
packages/editor/src/components/ui/action-menu/view-toggles.tsx), so the
`/editor/` prefix has to survive the hop. `/scene/<id>` keeps working
for the app's own links, and the MCP-side tests asserting `/editor/<id>`
stay valid, so no test or storage changes are needed.

Also pass `--hostname 0.0.0.0` explicitly to `next dev`. Next already
binds every interface by default, so this pins that behaviour rather
than changing it, and surfaces the Network URL in dev output. Reaching
the dev server from another device still requires an inbound firewall
rule for port 3002.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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