jackwener/OpenCLI · error · CommandExecutionError
Codex picker selection did not reach the composer.
Error message
Codex picker selection did not reach the composer.
What it means
After the picker option click succeeds, the library polls `readComposerState` up to 20 times to confirm the composer now holds a chip matching the picked option. If no state shows a matching chip (`applied` is null or `chipMatchesLabel` fails), the selection silently failed and this error is thrown, preventing a send of the wrong content.
Source
Thrown at clis/codex/send.js:203
})()`));
if (clicked !== true) {
throw new CommandExecutionError('Codex picker option disappeared before it could be clicked.', `wanted=${picked.title}`);
}
// The click above is deferred, so verify it landed: the picker must
// close and the composer must hold a chip for the picked option.
let applied = null;
for (let attempt = 0; attempt < 20; attempt += 1) {
const state = await readComposerState(page);
const pickerOpen = unwrapEvaluateResult(await page.evaluate(`Array.from(document.querySelectorAll('${PICKER_ITEM_SELECTOR}')).some((it) => it instanceof HTMLElement && !!it.offsetParent)`)) === true;
if (!pickerOpen && state.chips.some((chip) => chipMatchesLabel(chip, picked.title))) {
applied = state;
break;
}
applied = state;
await page.wait(0.1);
}
if (!applied || !applied.chips.some((chip) => chipMatchesLabel(chip, picked.title))) {
throw new CommandExecutionError(
'Codex picker selection did not reach the composer.',
`expected=${picked.title} ${describeComposerState(applied ?? { text: '', chips: [] })}`,
);
}
}
await page.pressKey('Enter');
let state = await pollComposerState(page);
if (state.text) {
// The picker consumes Enter to insert the highlighted entry, so a
// still-loaded composer is either the untouched draft or the chip
// the picker resolved. Anything else was rewritten: never submit.
const slashToken = /^\/\S+$/.test(textToInsert) ? textToInsert.slice(1) : '';
const chipLabel = picked ? picked.title : slashToken;
const unchangedDraft = state.chips.length === 0 && state.text === String(textToInsert).trim();
const pickerResolvedCommand = chipLabel !== ''
&& state.chips.length === 1
&& state.nonChipText === ''
&& chipMatchesLabel(state.chips[0], chipLabel);View on GitHub (pinned to 49907e53dc)
Solutions
- Re-run the send; transient click/apply races usually resolve on retry.
- Check that the `--pick <label>` text matches the picker option label exactly (case/spacing).
- Press Escape in Codex to clear any half-applied state, then retry.
- If it persists after a Codex update, the chip rendering changed — update or patch `chipMatchesLabel`.
Example fix
// before
await runCodexSend({ pick: 'Plan Review' }); // label mismatch
// after
await runCodexSend({ pick: 'Plan review' }); // exact picker label Defensive patterns
Strategy: validation
Validate before calling
// Ensure the pick label matches an existing picker option before sending
const labels = await readPickerOptionLabels(page);
if (!labels.includes(pickLabel)) throw new Error(`Pick label '${pickLabel}' not in options: ${labels.join(', ')}`); Type guard
const isAppliedState = (s) => s != null && Array.isArray(s.chips) && s.chips.some((c) => chipMatchesLabel(c, expectedTitle));
Try / catch
try {
await codexSend({ pick: label });
} catch (e) {
if (String(e.message).includes('did not reach the composer')) {
await page.pressKey('Escape'); // reset composer
// verify label then retry once
} else throw e;
} Prevention
- Use the exact picker option label (case, spacing, punctuation).
- Press Escape to clear any half-applied picker state before retrying.
- Avoid running sends while the Codex page is re-rendering.
- After a Codex update, verify chip labels still match via composer state.
When it happens
Trigger: Clicking a picker option did not insert its chip into the composer within the polling window — the click landed on a non-interactive overlay, the chip label differs from `picked.title`, or the picker was closed without applying the selection.
Common situations: Codex UI version changed so chips render differently or labels are truncated differently; click hit an overlay instead of the option; picker selection applied to a different composer than the one being polled.
Related errors
- Codex picker option disappeared before it could be clicked.
- Codex send was not submitted.
- --pick label cannot be empty
- Picker option "${rawLabel}" is ambiguous.
- Codex picker did not open after typing the command.
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/befb1f9ce6cbd95f.
Report an issue: GitHub.