jackwener/OpenCLI · critical · CommandExecutionError

Failed to download grok image

Error message

Failed to download grok image ${img.src}${reason}

What it means

saveImages throws CommandExecutionError when an individual image fetch from assets.grok.com fails (non-OK HTTP or fetch error). The library fails loudly instead of writing a '[DOWNLOAD FAILED]' sentinel row because assets.grok.com is Cloudflare-gated — one 401/403 usually means the entire batch is unrecoverable. The remediation hint points at the live grok.com browser session.

Solutions

  1. Verify the grok.com tab the automation drives is logged in, then retry.
  2. Re-run the command — Cloudflare/CDN failures are often transient.
  3. Regenerate the image if its asset URL has expired.
  4. If a corporate proxy/VPN interferes, retry from a network that can reach assets.grok.com.

Example fix

// before (logged-out tab)
cli image --out ./imgs   # 401 from assets.grok.com
// after
// log into grok.com in the automated browser tab, then
cli image --out ./imgs
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check session before generating images:
const loggedIn = await page.evaluate(() => !!document.querySelector('[data-testid="composer"]'));
if (!loggedIn) throw new Error('grok.com session appears logged out; log in before downloading images');

Try / catch

try {
  const saved = await cli.image({ prompt, outDir });
} catch (e) {
  if (e instanceof CommandExecutionError && e.message.startsWith('Failed to download grok image')) {
    console.error('assets.grok.com blocked the download — log into grok.com in the automated tab and retry.');
  } else throw e;
}

Prevention

When it happens

Trigger: Downloading generated images while the fetch to assets.grok.com returns !ok (401/403/404/timeouts) or the fetch wrapper reports an error; saveImages is invoked by the image command after generation when an outDir is provided.

Common situations: Expired or logged-out grok.com browser session so Cloudflare blocks asset requests; transient CDN 5xx; hotlink protection; the image URL expiring shortly after generation.

Related errors


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

Appendix: source

Thrown at clis/grok/image.js:214

      let binary = '';
      for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
      return { ok: true, base64: btoa(binary), contentType: blob.type || 'image/jpeg' };
    } catch (e) { return { ok: false, error: e && e.message || String(e) }; }
  })()`);
}

async function saveImages(page, images, outDir) {
  fs.mkdirSync(outDir, { recursive: true });
  const results = [];
  for (const img of images) {
    const fetched = await fetchImageAsBase64(page, img.src);
    if (!fetched || !fetched.ok) {
      // Fail loudly on per-image download failure rather than emit a sentinel
      // row with path = '[DOWNLOAD FAILED] ...' that downstream tools cannot
      // distinguish from a real path. assets.grok.com is Cloudflare-gated, so
      // a single 401/403 typically means the whole batch is unrecoverable.
      const reason = fetched?.error ? `: ${fetched.error}` : '';
      throw new CommandExecutionError(
        `Failed to download grok image ${img.src}${reason}`,
        'assets.grok.com download requires the live grok.com browser session — verify the tab is logged in and try again.',
      );
    }
    const filepath = path.join(outDir, buildFilename(img.src, fetched.contentType));
    fs.writeFileSync(filepath, Buffer.from(fetched.base64 || '', 'base64'));
    results.push({ ...img, path: filepath });
  }
  return results;
}

function toRow(img, savedPath = '') {
  return { url: img.src, width: img.w, height: img.h, path: savedPath };
}

export const imageCommand = cli({
  site: 'grok',
  name: 'image',

View on GitHub (pinned to 49907e53dc)