musistudio/claude-code-router · warning · Error
Another Agent request is already waiting for this conversati
Error message
Another Agent request is already waiting for this conversation.
What it means
The middleware serializes outbound bot interaction requests per conversation: while one permission-approval or input request is pending, botPendingApprovals already contains the key and any second interaction for the same conversation throws immediately. This prevents competing approval prompts within one conversation.
Source
Thrown at packages/core/src/agents/codex/cli-middleware-runtime.ts:3877
const response = method === "item/permissions/requestApproval"
? claudeControlPermissionResponse(message, requestId, approval)
: claudeControlElicitationResponse(requestId, approval);
child.stdin.write(JSON.stringify(response) + "\n");
}).catch((error) => {
child.stdin.write(JSON.stringify({ type: "control_response", response: { subtype: "error", request_id: requestId, error: formatError(error) } }) + "\n");
});
}
async requestBotControl(work, requestId, method, params) {
const context = work.botContext;
const key = context.conversationKey;
if (method === "item/permissions/requestApproval" && !context.bridge.config.shellEnabled && isShellPermissionRequest(params)) {
return { decision: "deny", reason: "Agent shell tools are disabled in Bot Settings." };
}
if (method === "item/permissions/requestApproval" && this.botSessionApprovals.has(key)) {
return { decision: "allow", scope: "session" };
}
if (this.botPendingApprovals.has(key)) throw new Error("Another Agent request is already waiting for this conversation.");
const isPermission = method === "item/permissions/requestApproval";
const label = isPermission ? String(params.reason || "Agent permission") : String(params.message || "Agent input request");
const fallbackText = isPermission
? label + "\nReply /session approve, /session approve session, or /session deny."
: label + "\nReply /session answer <text> or /session deny.";
const actions = isPermission
? [
{ type: "button", label: "Approve once", value: "/session approve" },
{ type: "button", label: "Approve for session", value: "/session approve session" },
{ type: "button", label: "Deny", value: "/session deny" }
]
: [{ type: "button", label: "Deny", value: "/session deny" }];
const promise = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
if (this.botPendingApprovals.get(key) && this.botPendingApprovals.get(key).requestId === requestId) {
this.botPendingApprovals.delete(key);
}
reject(new Error("Bot approval timed out."));View on GitHub (pinned to 99f24806c6)
Solutions
- Answer or deny the pending request first (/session approve or /session deny), which clears the entry
- Design callers to queue, not parallelize, interactions per conversation
- If the entry is stale after a dropped response, restart the session/runtime to clear botPendingApprovals
- Add client-side dedupe on the conversation key before issuing a second request
Example fix
// before
await bridge.request("item/permissions/requestApproval", { ... }); // second call throws
// after
if (!bridge.botPendingApprovals.has(conversationKey)) {
await bridge.request("item/permissions/requestApproval", { ... });
} else {
queueUntilApprovalSettled(conversationKey, request); // serialize per conversation
} Defensive patterns
Strategy: validation
Validate before calling
if (bridge.botPendingApprovals.has(conversationKey)) { /* queue or surface existing prompt instead of issuing another */ } Type guard
null
Try / catch
try { await requestApproval(params); } catch (e) { if (/already waiting for this conversation/.test(String(e))) { await answerPendingApproval(key); } else throw e; } Prevention
- Serialize approval/input requests per conversation
- Always resolve pending prompts before triggering new agent actions
- Clear stale pending entries on session restart or disconnect
When it happens
Trigger: Issuing item/permissions/requestApproval or an input request for a conversation that already has an unanswered pending request (key = conversation), e.g. the agent requests two tool approvals in quick succession, or a user triggers a new action before answering the previous prompt.
Common situations: Parallel tool calls needing approval; user ignores the first prompt and sends another message; approval response lost (timeout/disconnect) so the entry is never cleared; UI bug that double-fires the request.
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/05f86271ef15b4f6.
Report an issue: GitHub.