musistudio/claude-code-router · error · Error

Bot Gateway SDK client does not expose request().

Error message

Bot Gateway SDK client does not expose request().

What it means

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.

Source

Thrown at packages/core/src/agents/bot-gateway/qr-login-service.ts:194

  return { canceled: Boolean(session) };
}

async function createQrClient(bot: BotGatewayRuntimeConfig, stateDir: string): Promise<BotGatewayClientWithRequest> {
  const sdk = await loadBotGatewaySdk();
  const command = resolveBotGatewayCommand(sdk, bot);
  const { electronRunAsNode, ...commandOptions } = command ?? {};
  const client = sdk.createBotGatewayClient({
    transport: "stdio",
    env: {
      ...process.env,
      ...(electronRunAsNode ? { ELECTRON_RUN_AS_NODE: "1" } : {}),
      BOT_GATEWAY_STATE_DIR: stateDir,
      CODEXL_HOME: CONFIGDIR
    },
    ...commandOptions
  }) as BotGatewayClientWithRequest;
  if (!client || typeof client.request !== "function" || typeof client.health !== "function") {
    throw new Error("Bot Gateway SDK client does not expose request().");
  }
  attachBotGatewayStdioErrorHandler(client);
  return client;
}

function resolveBotGatewayCommand(sdk: BotGatewaySdkModule, bot: BotGatewayRuntimeConfig): BotGatewayCommand | undefined {
  if (bot.command) {
    return {
      args: bot.args,
      command: resolveUserPath(bot.command),
      cwd: bot.cwd ? resolveUserPath(bot.cwd) : process.cwd()
    };
  }
  if (typeof sdk.bundledStdioPath !== "function") {
    return undefined;
  }
  const bundledPath = sdk.bundledStdioPath();
  const runnerPath = materializeBotGatewayStdioRunnerPath(bundledPath);

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Pin or upgrade @the-next-ai/bot-gateway-sdk to the exact version this code targets (check the repo lockfile)
  2. If the SDK now returns a Promise from createBotGatewayClient, await it before the check
  3. Inspect stdio error logs (attachBotGatewayStdioErrorHandler) to confirm the gateway process actually spawned with the right env (BOT_GATEWAY_STATE_DIR, CODEXL_HOME)
  4. If you own the SDK, restore request()/health() or update this adapter to the new client API

Example fix

// before
const client = createBotGatewayClient({...}) as BotGatewayClientWithRequest;
if (!client || typeof client.request !== "function" || typeof client.health !== "function") {
  throw new Error("Bot Gateway SDK client does not expose request().");
}

// after — handle Promise-returning SDK versions
const maybeClient = createBotGatewayClient({...});
const client = (maybeClient as Awaited<typeof maybeClient> | BotGatewayClientWithRequest) instanceof Promise
  ? await maybeClient
  : maybeClient as BotGatewayClientWithRequest;
if (!client || typeof client.request !== "function" || typeof client.health !== "function") {
  throw new Error("Bot Gateway SDK client does not expose request().");
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify the SDK surface before creating the client-dependent flow
const sdk = await import("@the-next-ai/bot-gateway-sdk");
if (typeof sdk.createBotGatewayClient !== "function") {
  throw new Error("Incompatible bot-gateway SDK: createBotGatewayClient missing");
}

Type guard

function isBotGatewayClientWithRequest(value: unknown): value is BotGatewayClientWithRequest {
  return typeof value === "object" && value !== null &&
    typeof (value as BotGatewayClientWithRequest).request === "function" &&
    typeof (value as BotGatewayClientWithRequest).health === "function";
}

Try / catch

try {
  const client = await createQrClient(bot, options);
} catch (error) {
  if (error instanceof Error && error.message.includes("does not expose request()")) {
    // check installed SDK version vs expected; reinstall/rebuild
  } else throw error;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/d9e5d0368d55522c. Report an issue: GitHub.