jackwener/OpenCLI · warning · EmptyResultError

The Gmail thread has no attachments

Error message

The Gmail thread has no attachments

What it means

The gmail attachments command fetched the requested thread successfully but flattening all messages produced zero attachments, so it throws EmptyResultError('gmail attachments', ...). This distinguishes a valid, reachable thread that simply has no files attached from a fetch failure.

Source

Thrown at clis/gmail/attachments.js:28

  domain: 'mail.google.com',
  strategy: Strategy.INTERCEPT,
  browser: true,
  navigateBefore: false,
  siteSession: 'persistent',
  args: [
    { name: 'thread', type: 'string', positional: true, required: true, help: 'Thread id from gmail search, legacy id, or Gmail thread URL' },
    { name: 'account', type: 'int', default: 0, help: 'Gmail account index from the /mail/u/<index>/ URL' },
  ],
  columns: ['messageId', 'attachmentId', 'name', 'mimeType', 'size'],
  func: async (page, kwargs) => {
    if (!page) throw new CommandExecutionError('Browser session required for gmail attachments');
    const messages = await fetchThread(page, kwargs.thread, parseAccount(kwargs.account));
    const rows = messages.flatMap((message) => message.attachments.map((attachment) => ({
      messageId: message.messageId,
      ...attachment,
    })));
    if (rows.length === 0) {
      throw new EmptyResultError('gmail attachments', 'The Gmail thread has no attachments');
    }
    return rows;
  },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the thread id/URL corresponds to a conversation that actually has attachments in the Gmail UI
  2. Check the `account` index matches the /mail/u/<index>/ profile that owns the thread
  3. Catch EmptyResultError and treat 'no attachments' as a valid business outcome rather than a crash
  4. Re-run gmail search filtered to has:attachment to get valid thread ids

Example fix

// before
const rows = await run('gmail-attachments', { thread: id });
// after
let rows;
try {
  rows = await run('gmail-attachments', { thread: id });
} catch (e) {
  if (isEmptyResultError(e)) rows = [];
  else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const thread = await runGmailSearch({ query: `rfc822msgid:${id} has:attachment` });
if (!thread || thread.attachmentCount === 0) {
  console.warn('thread has no attachments; skip attachments command');
}

Type guard

function hasAttachments(messages) {
  return Array.isArray(messages) && messages.some(m => Array.isArray(m.attachments) && m.attachments.length > 0);
}

Try / catch

try {
  rows = await runGmailAttachments(threadId);
} catch (e) {
  if (e instanceof EmptyResultError) rows = []; // no attachments is a valid outcome
  else throw e;
}

Prevention

When it happens

Trigger: fetchThread(page, kwargs.thread, parseAccount(kwargs.account)) returns messages whose combined attachments arrays are empty — e.g. passing a thread id/URL for a plain text conversation with no attachments.

Common situations: Wrong thread id copied from search results (picked a text-only thread); thread contains inline images that Gmail does not expose as downloadable attachments; legacy id vs new id mix-up pointing at a different thread; account index mismatch so a different (attachment-less) thread is loaded.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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