musistudio/claude-code-router · warning · Error
微信扫码登录会话不存在,请重新生成二维码。
Error message
微信扫码登录会话不存在,请重新生成二维码。
What it means
waitBotGatewayQrLogin() resolves the QR login session by sessionId in an in-memory map (qrSessions). Unknown IDs — never created, expired and evicted, or lost to a process restart — cannot be waited on, so the user is told (in Chinese) the WeChat QR session does not exist and to regenerate the QR code.
Source
Thrown at packages/core/src/agents/bot-gateway/qr-login-service.ts:128
sessionId,
stateDir,
tenantId: bot.tenantId
};
} catch (error) {
if (!registered) {
closeQrClient(client);
}
throw error;
}
}
export async function waitBotGatewayQrLogin(
request: BotGatewayQrLoginWaitRequest
): Promise<BotGatewayQrLoginWaitResult> {
const sessionId = request.sessionId.trim();
const session = qrSessions.get(sessionId);
if (!session) {
throw new Error("微信扫码登录会话不存在,请重新生成二维码。");
}
const rawWait = await botGatewayClientRequest(session.client, "auth.qr.wait", {
autoStart: true,
config: session.integrationConfig,
configOverride: {
...session.integrationConfig,
transport: botGatewayWebSocketTransport(session.platform)
},
credentials: session.credentials,
integrationId: session.integrationId,
platform: session.platform,
sessionId,
tenantId: session.tenantId,
timeoutMs: Math.max(1000, request.timeoutMs || 5000),
verifyCode: request.verifyCode?.trim() || undefined
}, session.timeoutMs);
const auth = unwrapGatewayResult(rawWait);View on GitHub (pinned to 99f24806c6)
Solutions
- Catch the error and call startBotGatewayQrLogin() again to generate a fresh QR, then wait on the new sessionId
- Confirm you pass the exact sessionId from the start result, not another identifier
- Check whether the core process restarted between start and wait (sessions are in-memory only); use sticky routing if running multiple instances
Example fix
// before
const result = await waitBotGatewayQrLogin({ sessionId });
// after
try {
const result = await waitBotGatewayQrLogin({ sessionId });
} catch (error) {
if (error instanceof Error && error.message.includes("会话不存在")) {
const started = await startBotGatewayQrLogin({ /* original request */ });
sessionId = started.sessionId; // show new QR, then wait again
} else {
throw error;
}
} Defensive patterns
Strategy: fallback
Validate before calling
// Re-verify session freshness before a long wait; ultimately rely on fallback restart // (no public accessor exists for qrSessions, so pre-validation is limited to using the id from start())
Try / catch
try {
return await waitBotGatewayQrLogin({ sessionId });
} catch (error) {
if (error instanceof Error && error.message.includes("会话不存在")) {
const started = await startBotGatewayQrLogin(originalRequest);
return await waitBotGatewayQrLogin({ sessionId: started.sessionId });
}
throw error;
} Prevention
- Always use the sessionId returned by startBotGatewayQrLogin() verbatim
- Treat any server restart as invalidating all QR sessions and restart the flow
- Show QR expiry in the UI so users rescan promptly instead of polling a dead session
When it happens
Trigger: Calling waitBotGatewayQrLogin() with a sessionId that was never returned by startBotGatewayQrLogin(), a stale sessionId from before a process restart/hot-reload, an expired/evicted session, or mixing up sessionId with botConfigId/integrationId.
Common situations: Frontend keeps polling after the server reloaded or redeployed; the wait outlived the QR session TTL; user returned to a stale page; multiple backend instances so the session lives in another process's memory.
Related errors
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/8d6c6d1aa420d1f2.
Report an issue: GitHub.