{"record":{"id":"d9e5d0368d55522c","repo":"musistudio/claude-code-router","slug":"bot-gateway-sdk-client-does-not-expose-request","errorCode":null,"errorMessage":"Bot Gateway SDK client does not expose request().","messagePattern":"Bot Gateway SDK client does not expose request\\(\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/agents/bot-gateway/qr-login-service.ts","lineNumber":194,"sourceCode":"  return { canceled: Boolean(session) };\n}\n\nasync function createQrClient(bot: BotGatewayRuntimeConfig, stateDir: string): Promise<BotGatewayClientWithRequest> {\n  const sdk = await loadBotGatewaySdk();\n  const command = resolveBotGatewayCommand(sdk, bot);\n  const { electronRunAsNode, ...commandOptions } = command ?? {};\n  const client = sdk.createBotGatewayClient({\n    transport: \"stdio\",\n    env: {\n      ...process.env,\n      ...(electronRunAsNode ? { ELECTRON_RUN_AS_NODE: \"1\" } : {}),\n      BOT_GATEWAY_STATE_DIR: stateDir,\n      CODEXL_HOME: CONFIGDIR\n    },\n    ...commandOptions\n  }) as BotGatewayClientWithRequest;\n  if (!client || typeof client.request !== \"function\" || typeof client.health !== \"function\") {\n    throw new Error(\"Bot Gateway SDK client does not expose request().\");\n  }\n  attachBotGatewayStdioErrorHandler(client);\n  return client;\n}\n\nfunction resolveBotGatewayCommand(sdk: BotGatewaySdkModule, bot: BotGatewayRuntimeConfig): BotGatewayCommand | undefined {\n  if (bot.command) {\n    return {\n      args: bot.args,\n      command: resolveUserPath(bot.command),\n      cwd: bot.cwd ? resolveUserPath(bot.cwd) : process.cwd()\n    };\n  }\n  if (typeof sdk.bundledStdioPath !== \"function\") {\n    return undefined;\n  }\n  const bundledPath = sdk.bundledStdioPath();\n  const runnerPath = materializeBotGatewayStdioRunnerPath(bundledPath);","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/agents/bot-gateway/qr-login-service.ts#L176-L212","documentation":"createQrClient() instantiates the Bot Gateway SDK client and duck-types the result: it must expose callable request() and health() methods. If the SDK's client shape changed — different method names, an unawaited Promise, or a falsy object because spawning failed — this guard fires immediately instead of producing a confusing TypeError later.","triggerScenarios":"Loading an @the-next-ai/bot-gateway-sdk version whose createBotGatewayClient returns an object without request/health (renamed API), returns a Promise that must be awaited, or returns undefined/throws internally leaving client falsy after the cast to BotGatewayClientWithRequest.","commonSituations":"SDK major version bump in the lockfile without updating this service; resolution picking up an unexpected SDK copy (bundled vs node_modules); gateway binary spawn failing silently so the client is never fully constructed; refactor in the SDK to a class-based client.","solutions":["Pin or upgrade @the-next-ai/bot-gateway-sdk to the exact version this code targets (check the repo lockfile)","If the SDK now returns a Promise from createBotGatewayClient, await it before the check","Inspect stdio error logs (attachBotGatewayStdioErrorHandler) to confirm the gateway process actually spawned with the right env (BOT_GATEWAY_STATE_DIR, CODEXL_HOME)","If you own the SDK, restore request()/health() or update this adapter to the new client API"],"exampleFix":"// before\nconst client = createBotGatewayClient({...}) as BotGatewayClientWithRequest;\nif (!client || typeof client.request !== \"function\" || typeof client.health !== \"function\") {\n  throw new Error(\"Bot Gateway SDK client does not expose request().\");\n}\n\n// after — handle Promise-returning SDK versions\nconst maybeClient = createBotGatewayClient({...});\nconst client = (maybeClient as Awaited<typeof maybeClient> | BotGatewayClientWithRequest) instanceof Promise\n  ? await maybeClient\n  : maybeClient as BotGatewayClientWithRequest;\nif (!client || typeof client.request !== \"function\" || typeof client.health !== \"function\") {\n  throw new Error(\"Bot Gateway SDK client does not expose request().\");\n}","handlingStrategy":"type-guard","validationCode":"// Verify the SDK surface before creating the client-dependent flow\nconst sdk = await import(\"@the-next-ai/bot-gateway-sdk\");\nif (typeof sdk.createBotGatewayClient !== \"function\") {\n  throw new Error(\"Incompatible bot-gateway SDK: createBotGatewayClient missing\");\n}","typeGuard":"function isBotGatewayClientWithRequest(value: unknown): value is BotGatewayClientWithRequest {\n  return typeof value === \"object\" && value !== null &&\n    typeof (value as BotGatewayClientWithRequest).request === \"function\" &&\n    typeof (value as BotGatewayClientWithRequest).health === \"function\";\n}","tryCatchPattern":"try {\n  const client = await createQrClient(bot, options);\n} catch (error) {\n  if (error instanceof Error && error.message.includes(\"does not expose request()\")) {\n    // check installed SDK version vs expected; reinstall/rebuild\n  } else throw error;\n}","preventionTips":["Lock the SDK version in the lockfile and verify it in CI","Write a unit test asserting the client shape from the pinned SDK version","Await any Promise-returning createBotGatewayClient before duck-typing"],"tags":["bot-gateway","sdk-compatibility","duck-typing","version-mismatch"],"backgroundTag":"sdk-interface-mismatch","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}