jackwener/OpenCLI · error · CommandExecutionError
Midjourney did not expose "${config.label}" for video ${jobI
Error message
Midjourney did not expose "${config.label}" for video ${jobId} What it means
A CommandExecutionError thrown when the injected in-page script cannot find a visible button whose trimmed text exactly equals the expected menu label (e.g. 'Download for Social' or 'Download as GIF') on the Midjourney video page. The library marks the matching menu item before clicking it, so absence of the button means Midjourney's UI does not offer that export for this video.
Source
Thrown at clis/midjourney/utils.js:753
if (!config) throw new ArgumentError('Rendered video kind must be video-social or gif');
await fs.mkdir(outputDir, { recursive: true });
const filePath = path.join(outputDir, `${jobId}_${index + 1}_${kind.replace('video-', '')}${config.extension}`);
const existing = await existingMedia(filePath, force, config.mime);
if (existing) return { index, kind, filePath, bytes: existing.size, url: null, mime: config.mime, cached: true };
await page.goto(jobUrl(jobId, index));
await page.wait({ selector: 'video[src]', timeout: 20 });
await page.click('button[title="Options"]');
await page.wait(0.3);
const marked = unwrapEvaluateResult(await page.evaluate((label) => {
document.querySelectorAll('[data-opencli-video-download]').forEach((node) => node.removeAttribute('data-opencli-video-download'));
const button = [...document.querySelectorAll('button[role="menuitem"],button')]
.find((node) => node.textContent?.trim() === label && node.getBoundingClientRect().width > 0);
if (!button) return false;
button.setAttribute('data-opencli-video-download', '1');
return true;
}, config.label));
if (!marked) throw new CommandExecutionError(`Midjourney did not expose "${config.label}" for video ${jobId}`);
await page.click('[data-opencli-video-download="1"]');
if (typeof page.waitForDownload !== 'function') {
throw new CommandExecutionError('Browser Bridge download lifecycle support is required for social video/GIF export');
}
const downloaded = await page.waitForDownload(jobId, 60_000);
if (!downloaded?.downloaded || downloaded.state !== 'complete' || !downloaded.filename) {
throw new CommandExecutionError(`Midjourney ${kind} download did not complete: ${downloaded?.error || downloaded?.state || 'unknown'}`);
}
const sourcePath = path.resolve(String(downloaded.filename));
const sourceStat = await fs.stat(sourcePath).catch(() => null);
if (!sourceStat?.isFile() || sourceStat.size <= 0) {
throw new CommandExecutionError(`Browser reported a completed download but the file is missing: ${sourcePath}`);
}
const handle = await fs.open(sourcePath, 'r');
let actualMime;
try {
const header = Buffer.alloc(16);
const { bytesRead } = await handle.read(header, 0, header.length, 0);View on GitHub (pinned to 49907e53dc)
Solutions
- Wait for the video page and its action menu to fully render before calling downloadRenderedVideo
- Verify the current Midjourney UI button text and update the label in the config map ('Download for Social' / 'Download as GIF')
- Confirm this video job actually supports social/GIF export (some video variants do not)
- Update your browser session/region so the UI renders the expected English labels
- Add a retry that opens the dropdown/menu first, then re-runs the marking script
Example fix
// before
await page.goto(jobUrl(jobId, index));
const marked = await page.evaluate(markScript, config.label);
// after
await page.goto(jobUrl(jobId, index));
await page.waitForSelector('button[role="menuitem"], button', { timeout: 30_000 });
const marked = await page.evaluate(markScript, config.label);
if (!marked) throw new CommandExecutionError(`Midjourney did not expose "${config.label}" for video ${jobId}`); Defensive patterns
Strategy: retry
Validate before calling
await page.goto(jobUrl(jobId, index), { waitUntil: 'networkidle' });
const hasButton = await page.evaluate(() =>
[...document.querySelectorAll('button')].some((b) =>
/Download (for Social|as GIF)/.test(b.textContent?.trim() || '') && b.getBoundingClientRect().width > 0));
if (!hasButton) throw new Error('Midjourney download menu not rendered yet — aborting before export'); Try / catch
try {
await downloadRenderedVideo(page, jobId, i, kind, outDir);
} catch (err) {
if (err.message.includes('did not expose')) {
await page.reload({ waitUntil: 'networkidle' });
await downloadRenderedVideo(page, jobId, i, kind, outDir);
} else throw err;
} Prevention
- Wait for the page/action menu to fully render (networkidle or a menu selector) before exporting
- Pin or monitor Midjourney UI changes; update button labels promptly after UI updates
- Confirm the video job type supports the requested export before attempting
- Use an English-locale browser profile so labels match the expected strings
When it happens
Trigger: page.evaluate marking script runs on the job page and returns false because no visible button matches config.label — Midjourney's dropdown has different text, the menu wasn't opened, or the video has no such export option.
Common situations: Midjourney shipped a UI update renaming or relocating the download menu items; the job is still processing or is a video type without social/GIF export; the page didn't finish loading before evaluate ran; regional/localized UI text differing from English labels.
Related errors
- Booking.com page declared results but no property cards were
- Mercury receipt upload did not confirm exactly one uploaded
- Mercury receipt upload did not confirm the intended receipt
- Midjourney did not expose a Describe action for the uploaded
- Midjourney Describe
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/fbdb1febdd351e3a.
Report an issue: GitHub.