hcengineering/platform · error

Router is not bound to an endpoint

Error message

Router is not bound to an endpoint

What it means

backrpc Server.getPort() waits for the server's bound promise, then reads router.lastEndpoint. If the router still has no endpoint (null), the router never actually bound to a socket even though binding resolved, so the server cannot report a port. This guards against reporting a port for an unbound router.

Source

Thrown at foundations/net/packages/backrpc/src/server.ts:119

        )
        this.handleClose(clientRecord.id, clientId, true)
      }
    }
  }

  private handleClose (clientRecordId: ClientT, clientId: string, timeout: boolean): void {
    void this.handlers.closeHandler?.(clientRecordId, timeout).catch((err) => {
      console.error('Error in handleTimeout', err)
    })
    this.revClientMapping.delete(clientId)
    this.clientMapping.delete(clientRecordId)
  }

  async getPort (): Promise<number> {
    await this.bound
    const reqEndpoint = this.router.lastEndpoint
    if (reqEndpoint === null) {
      throw new Error('Router is not bound to an endpoint')
    }

    const portMatch = reqEndpoint.match(/:(\d+)$/)
    const port = portMatch != null ? parseInt(portMatch[1]) : undefined
    if (port === undefined) {
      throw new Error('Router is not bound to a port')
    }
    return port
  }

  private sendPromise: Promise<void> | undefined

  async doSend (msg: any[]): Promise<void> {
    while (this.sendPromise !== undefined) {
      await this.sendPromise
    }
    this.sendPromise = this.router.send(msg)
    try {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Ensure the server is fully started (call the start/listen method and await it) before calling getPort().
  2. Inspect the transport implementation: verify bind() sets router.lastEndpoint before resolving its promise.
  3. Log router.lastEndpoint right after startup to confirm the endpoint is set in your environment.
  4. If the server may be closed, re-create/rebind it before querying the port.

Example fix

// before
await server.bound;
const port = await server.getPort(); // throws: endpoint null
// after
await server.start(); // actually binds the router
const port = await server.getPort();
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const port = await server.getPort();
} catch (e) {
  if (e.message === 'Router is not bound to an endpoint') {
    throw new Error('Server startup incomplete: await server.start() before getPort()');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling server.getPort() after awaiting this.bound when router.lastEndpoint is null — i.e. the transport/router bound promise resolved without establishing an endpoint, typically because listen/bind was never called or bound to nothing.

Common situations: Calling getPort() before start()/listen() has really attached the router to an endpoint; a transport implementation whose bind resolves without setting lastEndpoint; race where the server was closed between bind and getPort.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/e73d9b070023a5c6. Report an issue: GitHub.