jackwener/OpenCLI · error · CommandExecutionError

Midjourney composer did not switch from video to image mode

Error message

Midjourney composer did not switch from video to image mode

What it means

CommandExecutionError from the ensure-image-mode helper. It reads the composer mode; if it reads 'video' it clicks 'Switch to Image' via clickVisibleControl (which itself needs nativeClick) and waits, then re-checks. If the composer still reports 'video' after the attempt, it throws, meaning the mode switch did not take effect.

Source

Thrown at clis/midjourney/utils.js:1335

    return Boolean((label && visible(label)) || fast);
  })));
}

export async function ensureImageComposer(page) {
  const composerMode = async () => unwrapEvaluateResult(await page.evaluate(() => {
    const visible = (button) => Boolean(button && button.getBoundingClientRect().width > 0);
    if (visible(document.querySelector('button[title="Switch to Image"]'))) return 'video';
    if (visible(document.querySelector('button[title="Switch to Video"]'))) return 'image';
    return 'unknown';
  }));
  for (let attempt = 0; attempt < 3; attempt += 1) {
    const mode = await composerMode();
    if (mode !== 'video') return;
    await clickVisibleControl(page, 'Switch to Image');
    await page.wait(0.75);
  }
  if (await composerMode() === 'video') {
    throw new CommandExecutionError('Midjourney composer did not switch from video to image mode');
  }
}

export async function readSiteSettings(page) {
  const settingsVisible = await isSettingsPanelVisible(page);
  if (!settingsVisible) {
    await toggleSettingsPanel(page);
  }
  const result = unwrapEvaluateResult(await page.evaluate(() => {
    const visible = (element) => {
      const rect = element.getBoundingClientRect();
      return rect.width > 0 && rect.height > 0 && getComputedStyle(element).display !== 'none';
    };
    const exactNode = (text) => [...document.querySelectorAll('h2,a,div,span')]
      .filter((node) => node.textContent?.trim() === text && visible(node))
      .sort((left, right) => left.children.length - right.children.length)[0] || null;
    const isSelected = (button) => button.getAttribute('aria-pressed') === 'true'
      || button.getAttribute('aria-checked') === 'true'

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Ensure the browser bridge supports nativeClick so 'Switch to Image' is actually clicked
  2. Retry the switch with a longer wait after clicking
  3. Check the session/composer is in a state where mode switching is permitted (not mid-generation)
  4. Update the CLI for current 'Switch to Image' control markup

Example fix

// before
await ensureImageMode(page);
// after
for (let i = 0; i < 3; i++) {
  try { await ensureImageMode(page); return; }
  catch (e) { await page.wait(2); }
}
throw new Error('Composer stayed in video mode');
Defensive patterns

Strategy: retry

Validate before calling

// nothing to check pre-call beyond nativeClick support
if (typeof page.nativeClick !== 'function') throw new Error('nativeClick required for mode switch');

Try / catch

for (let attempt = 0; attempt < 3; attempt++) {
  try { await ensureImageMode(page); break; }
  catch (e) {
    if (!String(e.message).includes('video to image')) throw e;
    await page.wait(2);
  }
}

Prevention

When it happens

Trigger: Calling the image-mode enforcement routine while the composer is in video mode and either the 'Switch to Image' control is not found/clicked, the native click was swallowed by the UI, or composerMode() keeps returning 'video' due to DOM/DATA attribute changes.

Common situations: Slow UI transitions — the 0.75s wait is too short on a slow machine/network; session stuck in video mode; Midjourney renamed the switch control; bridge lacking nativeClick so the click silently did nothing.

Related errors


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