Yeachan-Heo/oh-my-codex · warning · Error

sendToWorker: submit_queued_after_tool_call

Error message

sendToWorker: submit_queued_after_tool_call

What it means

After a tool call, the worker's draft line was still queued rather than executed: the pane capture still matches paneHasQueuedCodexSubmission after a final double-Enter attempt. The submission is stuck behind the worker's post-tool-call state, so the library reports submit_queued_after_tool_call instead of silently assuming success.

Source

Thrown at src/team/tmux-session.ts:3870

  const [verifyCapture, verifyVisibleCapture] = await Promise.all([
    capturePaneAsync(resolveTarget),
    captureVisiblePaneAsync(resolveTarget),
  ]);
  if (verifyCapture) {
    if (paneHasActiveTask(verifyCapture)) return;
    if (
      !normalizeWorkerTriggerForDraftMatch(verifyCapture).includes(normalizeWorkerTriggerForDraftMatch(text))
      && !paneHasQueuedCodexSubmission(verifyVisibleCapture)
    ) {
      return;
    }
    // Draft still visible and no active task — one more Enter attempt.
    await sendKeyAsync(resolveTarget, 'Enter');
    await sleep(150);
    await sendKeyAsync(resolveTarget, 'Enter');
    const finalVisibleCapture = await captureVisiblePaneAsync(resolveTarget);
    if (paneHasQueuedCodexSubmission(finalVisibleCapture)) {
      throw new Error('sendToWorker: submit_queued_after_tool_call');
    }
    const finalCapture = await capturePaneAsync(resolveTarget);
    if (
      normalizeWorkerTriggerForDraftMatch(finalCapture).includes(normalizeWorkerTriggerForDraftMatch(text))
      && !paneHasActiveTask(finalCapture)
      && paneLooksReady(finalCapture)
    ) {
      throw new Error('sendToWorker: submit_failed (trigger text still visible after retries)');
    }
  }
}

export function notifyLeaderStatus(sessionName: string, message: string): boolean {
  if (!isTmuxAvailable()) return false;
  const trimmed = message.trim();
  if (!trimmed) return false;
  const capped = trimmed.length > 180 ? `${trimmed.slice(0, 177)}...` : trimmed;
  const result = runTmux(['display-message', '-t', sessionName, '--', capped]);

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Wait until the pane shows an idle prompt (paneLooksReady / no active task) before submitting
  2. Retry the whole submit after a delay once the tool call finishes
  3. Avoid sending triggers while paneHasActiveTask is true

Example fix

// before
await sendToWorker(resolveTarget, text); // during tool call

// after
await waitFor(async () => paneLooksReady(await capturePaneAsync(resolveTarget)));
await sendToWorker(resolveTarget, text);
Defensive patterns

Strategy: retry

Validate before calling

const cap = await capturePaneAsync(resolveTarget);
if (paneHasActiveTask(cap)) await waitForWorkerIdle(resolveTarget);

Try / catch

try { await submitWorker(resolveTarget, text); } catch (err) { if (!/submit_queued_after_tool_call/.test(String(err))) throw err; await sleep(1500); await submitWorker(resolveTarget, text); }

Prevention

When it happens

Trigger: Sending a trigger while the worker is finishing a tool call; the Enter presses get buffered as a queued Codex submission; final capture still shows the queued draft line, so the error is thrown.

Common situations: Race between tool-call completion and user/leader input; pressing Enter during Codex's spinner state; slow tool execution (npm install, test run) where keystrokes queue up.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/a346d7386c6bc428. Report an issue: GitHub.