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
- Verify the thread id/URL corresponds to a conversation that actually has attachments in the Gmail UI
- Check the `account` index matches the /mail/u/<index>/ profile that owns the thread
- Catch EmptyResultError and treat 'no attachments' as a valid business outcome rather than a crash
- 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
- Pick thread ids from searches filtered with has:attachment
- Confirm the thread shows attachments in the Gmail UI first
- Match the account index (/mail/u/<index>/) to the thread's owner
- Handle EmptyResultError as business-as-usual in pipelines
- Beware inline images — they may not count as attachments
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
- gmail search
- gmail labels
- gmail thread
- No prices returned for train_no=${trainNo} ${fromStation.nam
- No 12306 stations match "${keyword}"
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/b7a4c4dd978201e8.
Report an issue: GitHub.