hcengineering/platform · error

Client ${clientId as string} not found

Error message

Client ${clientId as string} not found

What it means

BackRPC server's send() pushes a one-way event to a connected client, but clientId has no entry in clientMapping, so no transport identity exists to deliver to. It throws instead of silently dropping the event. Same root cause as request(): unknown or disconnected client.

Source

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

    if (clientIdentity === undefined) {
      throw new Error(`Client ${clientId} not found`)
    }
    return await new Promise<any>((resolve, reject) => {
      const reqId = clientId + '-' + this.requestCounter++
      this.backRequests.set(reqId, { resolve, reject })

      void this.doSend([clientIdentity, backrpcOperations.request, reqId, stringifyJSON([method, params])]).catch(
        (err) => {
          reject(err)
        }
      )
    })
  }

  async send (clientId: ClientT, body: any): Promise<any> {
    const clientIdentity = this.clientMapping.get(clientId)
    if (clientIdentity === undefined) {
      throw new Error(`Client ${clientId as string} not found`)
    }
    await this.doSend([clientIdentity, backrpcOperations.event, '', stringifyJSON(body)])
  }

  async close (): Promise<void> {
    this.closed = true
    this.stopTick?.()
    this.router.close()
  }
}

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Prune stale clientIds from your registry on disconnect events before sending
  2. Re-establish the client connection and resend the event
  3. Wrap sends in try-catch and treat unknown-client as a disconnect signal
  4. Confirm the client connected to the same server instance handling the send

Example fix

// before
await server.send(clientId, { type: 'notify' })
// after
try {
  await server.send(clientId, { type: 'notify' })
} catch (e) {
  removeClient(clientId) // stale; drop or reconnect
}
Defensive patterns

Strategy: try-catch

Validate before calling

// skip sends for clients no longer tracked
if (!liveClients.has(clientId)) return

Type guard

function canSend(mapping: Map<ClientT, unknown>, clientId: ClientT): boolean {
  return mapping.get(clientId) !== undefined
}

Try / catch

try {
  await server.send(clientId, body)
} catch (e) {
  if (e instanceof Error && e.message.includes('not found')) {
    removeClient(clientId)
  } else { throw e }
}

Prevention

When it happens

Trigger: Calling server.send(clientId, body) for a clientId that never connected, already disconnected, or connected to a different server instance.

Common situations: Broadcast loops that iterate a cached client list without pruning disconnected ids; sending a final event after the client's close completed; restarting the server while clients reconnect with new ids.

Related errors


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