jackwener/OpenCLI · error · CommandExecutionError

Gmail fetch-data response had an unexpected shape

Error message

Gmail fetch-data response had an unexpected shape

What it means

parseFetchData validates the Gmail fetch-data (/i/fd) payload: it must be an array whose element [1] is an array of thread wrappers. Anything else triggers this CommandExecutionError. The /fd wire format differs from /bv, so this guard is the fetch-data counterpart of the batch-view shape check and fires when the response does not match the known layout.

Source

Thrown at clis/gmail/utils.js:243

  const out = [];
  for (const wrapper of Array.isArray(record?.[13]) ? record[13] : []) {
    const node = Array.isArray(wrapper?.[0]) ? wrapper[0] : null;
    const data = Array.isArray(node?.[3]) ? node[3] : null;
    const attachmentId = cleanString(node?.[1]);
    if (!data || !attachmentId) continue;
    out.push({
      attachmentId,
      name: cleanString(data[2]) || null,
      mimeType: cleanString(data[3]) || null,
      size: Number.isFinite(Number(data[4])) ? Number(data[4]) : null,
    });
  }
  return out;
}

export function parseFetchData(body) {
  if (!Array.isArray(body) || !Array.isArray(body[1])) {
    throw new CommandExecutionError('Gmail fetch-data response had an unexpected shape');
  }
  const messages = [];
  for (const threadWrapper of body[1]) {
    const threadId = cleanString(threadWrapper?.[0]).replace(/^#/, '');
    const rows = Array.isArray(threadWrapper?.[2]) ? threadWrapper[2] : [];
    for (const wrapper of rows) {
      const messageId = cleanString(wrapper?.[0]).replace(/^#/, '');
      const record = Array.isArray(wrapper?.[1]) ? wrapper[1] : null;
      if (!threadId || !messageId || !record) {
        throw new CommandExecutionError('Gmail fetch-data returned a malformed message');
      }
      const from = senderFromRecord(record);
      const attachments = parseAttachments(record);
      messages.push({
        messageId,
        legacyMessageId: cleanString(record[34]) || null,
        threadId,
        subject: cleanString(record[4]) || '(no subject)',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the command; if persistent, capture the payload and report it for a parser update.
  2. Update opencli to the latest version matching Gmail's current fd format.
  3. Reload Gmail, reopen the thread in the UI, then rerun so a clean fd response is captured.
  4. Avoid concurrent Gmail navigation while fetchThread's capture window is open.
Defensive patterns

Strategy: try-catch

Validate before calling

// validate the thread target before opening so a wrong navigation doesn't yield a stray fd payload
if (!/^(thread-f:\d+|[a-f\d]{10,}|#?\d+)$/i.test(target)) {
  throw new Error('target must be a Gmail thread id or thread URL');
}

Type guard

function isFetchDataEnvelope(body) {
  return Array.isArray(body) && Array.isArray(body[1]);
}

Try / catch

try {
  messages = await gmailThread(threadId);
} catch (error) {
  if (String(error.message).includes('fetch-data response had an unexpected shape')) {
    await page.reload(); // re-navigate cleanly, then retry once
    messages = await gmailThread(threadId);
  } else throw error;
}

Prevention

When it happens

Trigger: fetchThread captures a /i/fd response where body is not an array or body[1] is not an array — Gmail protocol change, capture matched a different /sync request, or a partial/interstitial payload was captured.

Common situations: Gmail web-app updates altering the fd envelope, opening a thread right as the page navigates so an unrelated fd response is captured, stale Gmail bundles after a deploy.

Related errors


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