{"record":{"id":"e73d9b070023a5c6","repo":"hcengineering/platform","slug":"router-is-not-bound-to-an-endpoint","errorCode":null,"errorMessage":"Router is not bound to an endpoint","messagePattern":"Router is not bound to an endpoint","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"foundations/net/packages/backrpc/src/server.ts","lineNumber":119,"sourceCode":"        )\n        this.handleClose(clientRecord.id, clientId, true)\n      }\n    }\n  }\n\n  private handleClose (clientRecordId: ClientT, clientId: string, timeout: boolean): void {\n    void this.handlers.closeHandler?.(clientRecordId, timeout).catch((err) => {\n      console.error('Error in handleTimeout', err)\n    })\n    this.revClientMapping.delete(clientId)\n    this.clientMapping.delete(clientRecordId)\n  }\n\n  async getPort (): Promise<number> {\n    await this.bound\n    const reqEndpoint = this.router.lastEndpoint\n    if (reqEndpoint === null) {\n      throw new Error('Router is not bound to an endpoint')\n    }\n\n    const portMatch = reqEndpoint.match(/:(\\d+)$/)\n    const port = portMatch != null ? parseInt(portMatch[1]) : undefined\n    if (port === undefined) {\n      throw new Error('Router is not bound to a port')\n    }\n    return port\n  }\n\n  private sendPromise: Promise<void> | undefined\n\n  async doSend (msg: any[]): Promise<void> {\n    while (this.sendPromise !== undefined) {\n      await this.sendPromise\n    }\n    this.sendPromise = this.router.send(msg)\n    try {","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/net/packages/backrpc/src/server.ts#L101-L137","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the server is fully started (call the start/listen method and await it) before calling getPort().","Inspect the transport implementation: verify bind() sets router.lastEndpoint before resolving its promise.","Log router.lastEndpoint right after startup to confirm the endpoint is set in your environment.","If the server may be closed, re-create/rebind it before querying the port."],"exampleFix":"// before\nawait server.bound;\nconst port = await server.getPort(); // throws: endpoint null\n// after\nawait server.start(); // actually binds the router\nconst port = await server.getPort();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const port = await server.getPort();\n} catch (e) {\n  if (e.message === 'Router is not bound to an endpoint') {\n    throw new Error('Server startup incomplete: await server.start() before getPort()');\n  }\n  throw e;\n}","preventionTips":["Always await the server's full start/listen call, not just the bound promise, before querying the port.","Assert router.lastEndpoint !== null after startup in smoke tests.","Avoid closing the server before reading its port."],"tags":["rpc","server","binding","networking"],"backgroundTag":"server-not-bound","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}