jackwener/OpenCLI · critical · CommandExecutionError

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

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.

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)

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.