Skip to content

Critical: Second application instance freezes when Go core is enabled (port conflict on :43001) #1077

Description

@ElioNeto

Description

When running two instances of the TeamCode application in different directories, the second instance freezes during startup if the Go core rewrite is enabled. This is a critical regression introduced by the Go core rewrite feature.

Root Cause

The Go core server (go-core/cmd/server/main.go) listens on a fixed port defined by the GO_CORE_PORT environment variable, defaulting to 43001:

port := os.Getenv("GO_CORE_PORT")
if port == "" {
    port = "43001"
}
// ...
server := &http.Server{Addr: ":" + port}

When the first instance starts, its Go core process successfully binds to :43001. When the second instance starts, its Go core process fails to bind because the port is already in use (EADDRINUSE). The Go process exits with an error, but the TypeScript spawner (packages/core/src/router/go-core.ts) does not detect this failure gracefully.

The Spawner Problem

In go-core.ts, the startGoCore() function:

  1. Spawns the Go binary with GO_CORE_PORT=43001 (line 78)
  2. Enters a 5-second polling loop waiting for the health endpoint (lines 97-112)
  3. Since the Go process already crashed, it never gets a health response
  4. After 5 seconds, it calls stopGoCore() and returns false

During those 5 seconds, the application appears frozen — no error is shown, no fallback to TypeScript is triggered immediately. After the timeout, Go core features are unavailable but the application may enter an inconsistent state where some routes are routed to a dead Go core while others use TypeScript, depending on feature flag configuration.

Additional Issues

  1. No port conflict detection: The Go core binary should detect port conflicts and exit with a specific error code that the TS spawner can interpret immediately (instead of waiting 5 seconds).

  2. No isolated port per instance: Each application instance should use its own port (e.g., derived from PID or a random available port).

  3. No graceful fallback: When Go core fails to start, all Go-routed operations should immediately fall back to the TypeScript implementation instead of hanging.

  4. No error propagation: The startGoCore() function does not capture or relay the Go process error output to the user.

Steps to Reproduce

  1. Enable Go core rewrite (set GO_CORE_BINARY env var or have the binary bundled)
  2. Open two terminal windows
  3. In terminal 1: teamcode (or bun dev) in /path/to/project-a
  4. In terminal 2, immediately: teamcode (or bun dev) in /path/to/project-b
  5. Observe that the second instance freezes for ~5 seconds and may remain partially functional

Expected Behavior

Each application instance should:

  • Use an independent port for its Go core process
  • Gracefully fall back to TypeScript if Go core fails to start
  • Show a meaningful error message if Go core cannot start

Environment

  • Go core version: current dev branch
  • Application: TeamCode (TypeScript + Go core rewrite)
  • Platform: Linux/macOS/Windows (all affected)

Suggested Fix

  1. Use a dynamic port — allocate a random available port per Go core process instead of hardcoding 43001:

    • Pass --port 0 to have the OS assign a free port, then read the actual port from stdout
    • Or use get-port (or equivalent) to find a free port before spawning
  2. Detect port conflict immediately — the Go binary should exit with code EADDRINUSE (or a specific exit code) when port binding fails, so the TS spawner can react immediately.

  3. Fail fast and fall back — if Go core cannot start within ~1 second, log the error and fall back to TypeScript immediately.

  4. Show error to user — print a clear error message when Go core fails to start.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcriticalphase:p0-criticalMust fix now — stability, security, core breaksscope:core-engineCore runtime, sessions, streaming

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions