{"record":{"id":"b7a4c4dd978201e8","repo":"jackwener/OpenCLI","slug":"the-gmail-thread-has-no-attachments","errorCode":null,"errorMessage":"The Gmail thread has no attachments","messagePattern":"The Gmail thread has no attachments","errorType":"error_code","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/gmail/attachments.js","lineNumber":28,"sourceCode":"  domain: 'mail.google.com',\n  strategy: Strategy.INTERCEPT,\n  browser: true,\n  navigateBefore: false,\n  siteSession: 'persistent',\n  args: [\n    { name: 'thread', type: 'string', positional: true, required: true, help: 'Thread id from gmail search, legacy id, or Gmail thread URL' },\n    { name: 'account', type: 'int', default: 0, help: 'Gmail account index from the /mail/u/<index>/ URL' },\n  ],\n  columns: ['messageId', 'attachmentId', 'name', 'mimeType', 'size'],\n  func: async (page, kwargs) => {\n    if (!page) throw new CommandExecutionError('Browser session required for gmail attachments');\n    const messages = await fetchThread(page, kwargs.thread, parseAccount(kwargs.account));\n    const rows = messages.flatMap((message) => message.attachments.map((attachment) => ({\n      messageId: message.messageId,\n      ...attachment,\n    })));\n    if (rows.length === 0) {\n      throw new EmptyResultError('gmail attachments', 'The Gmail thread has no attachments');\n    }\n    return rows;\n  },\n});\n","sourceCodeStart":10,"sourceCodeEnd":33,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/gmail/attachments.js#L10-L33","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst rows = await run('gmail-attachments', { thread: id });\n// after\nlet rows;\ntry {\n  rows = await run('gmail-attachments', { thread: id });\n} catch (e) {\n  if (isEmptyResultError(e)) rows = [];\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"const thread = await runGmailSearch({ query: `rfc822msgid:${id} has:attachment` });\nif (!thread || thread.attachmentCount === 0) {\n  console.warn('thread has no attachments; skip attachments command');\n}","typeGuard":"function hasAttachments(messages) {\n  return Array.isArray(messages) && messages.some(m => Array.isArray(m.attachments) && m.attachments.length > 0);\n}","tryCatchPattern":"try {\n  rows = await runGmailAttachments(threadId);\n} catch (e) {\n  if (e instanceof EmptyResultError) rows = []; // no attachments is a valid outcome\n  else throw e;\n}","preventionTips":["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"],"tags":["empty-result","gmail","attachments"],"backgroundTag":"empty-result-set","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}