jackwener/OpenCLI · error · CommandExecutionError

Could not find the ChatGPT model selector in the composer.

Error message

Could not find the ChatGPT model selector in the composer.

What it means

After ensuring the composer is visible, selectChatGPTModel evaluates a script to locate the model-selector button in the composer. If no matching element (by testid/aria-label/geometry) is found, menuButton.found stays false and the library throws CommandExecutionError. This guards against clicking at bogus coordinates.

Source

Thrown at clis/chatgpt/utils.js:650

        let button = Array.from(document.querySelectorAll('form button')).find((node) =>
            isVisible(node) && labels.some((label) => textMatchesLabel(node.textContent, label))
        );
        if (!button) {
            button = menuButtonSelectors
                .map((selector) => document.querySelector(selector))
                .find((node) => node instanceof HTMLElement && isVisible(node));
        }
        if (!button) return { found: false };
        button.scrollIntoView({ block: 'center', inline: 'center' });
        const rect = button.getBoundingClientRect();
        return {
            found: true,
            x: Math.round(rect.left + rect.width / 2),
            y: Math.round(rect.top + rect.height / 2),
        };
    })()`)), 'chatgpt model menu button');
    if (!menuButton.found) {
        throw new CommandExecutionError('Could not find the ChatGPT model selector in the composer.');
    }
    await page.nativeClick(Number(menuButton.x), Number(menuButton.y));
    await page.wait(0.5);

    let optionCenter = null;
    for (let attempt = 0; attempt < 10; attempt += 1) {
        optionCenter = requireObjectEvaluateResult(unwrapEvaluateResult(await page.evaluate(`(() => {
            const isVisible = (el) => {
                if (!(el instanceof HTMLElement)) return false;
                const style = window.getComputedStyle(el);
                if (style.display === 'none' || style.visibility === 'hidden') return false;
                const rect = el.getBoundingClientRect();
                return rect.width > 0 && rect.height > 0;
            };
            const normalize = (value) => String(value || '').replace(/\\s+/g, ' ').trim();
            const escapeRegExp = (value) => String(value).replace(/[|\\\\{}()[\\]^$+*?.]/g, '\\\\$&');
            const textMatchesLabel = (text, label) => {
                const normalizedText = normalize(text);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry after a short wait so the composer toolbar finishes rendering.
  2. Set OPENCLI_CHATGPT_MODEL_DEBUG=1 to inspect how far the flow got and what the page URL/state was.
  3. Open ChatGPT manually and confirm the model selector exists in your UI variant/locale; disable temporary-chat or special modes.
  4. Update the opencli package so its selector list matches the current ChatGPT DOM.
  5. If the API path applies to your target model, rely on setChatGPTModelConfig (API) rather than the picker fallback.

Example fix

// before
await selectChatGPTModel(page, 'balanced'); // fails when UI not hydrated
// after
await page.wait(2); // let composer toolbar render
await selectChatGPTModel(page, 'balanced');
Defensive patterns

Strategy: retry

Validate before calling

await ensureChatGPTComposer(page);
await page.wait(1.5); // let the composer toolbar render before locating the selector button

Type guard

function menuButtonFound(res) {
  return res != null && res.found === true && Number.isFinite(Number(res.x)) && Number.isFinite(Number(res.y));
}

Try / catch

for (let i = 0; i < 3; i++) {
  try { return await selectChatGPTModel(page, model); }
  catch (err) {
    if (!(err instanceof CommandExecutionError) || !/model selector/.test(err.message)) throw err;
    await page.wait(1.5);
  }
}
throw new Error('model selector never appeared');

Prevention

When it happens

Trigger: The model switcher button is absent from the composer DOM — typically a ChatGPT UI A/B variant or redesign, page not fully hydrated, locale rendering unexpected labels, the composer in a state where the selector is hidden (e.g. temporary chat, some compact layouts), or a CSP/environment blocking the evaluate script.

Common situations: ChatGPT frontend rollout changing data-testid of the model switcher; page checked too early before the composer toolbar renders; non-default ChatGPT modes (temporary chat, project canvases) that hide the model picker; layouts where the button rect is zero-sized.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/96708b4bcc780e2d. Report an issue: GitHub.