{"record":{"id":"fe6472dfd914c805","repo":"jackwener/OpenCLI","slug":"gmail-search-query-cannot-be-empty","errorCode":null,"errorMessage":"Gmail search query cannot be empty","messagePattern":"Gmail search query cannot be empty","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/gmail/utils.js","lineNumber":481,"sourceCode":"    throw new CommandExecutionError(`Gmail ${operation} requires native browser input`);\n  }\n  await page.nativeType(query);\n  const actual = unwrapBrowserResult(await page.evaluate(`() => document.querySelector('input[name=\"q\"]')?.value || ''`), `${operation} search input verification`);\n  if (actual !== query) throw new CommandExecutionError(`Gmail ${operation} could not set the search query exactly`);\n  if (typeof page.cdp === 'function') {\n    const key = { key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, nativeVirtualKeyCode: 13 };\n    await page.cdp('Input.dispatchKeyEvent', { type: 'rawKeyDown', ...key });\n    await page.cdp('Input.dispatchKeyEvent', { type: 'keyUp', ...key });\n  } else if (typeof page.nativeKeyPress === 'function') {\n    await page.nativeKeyPress('Enter');\n  } else {\n    throw new CommandExecutionError(`Gmail ${operation} requires native browser keyboard input`);\n  }\n}\n\nexport async function queryThreads(page, query, { account = 0, limit = DEFAULT_LIMIT } = {}) {\n  const normalizedQuery = cleanString(query);\n  if (!normalizedQuery) throw new ArgumentError('Gmail search query cannot be empty');\n  await ensureGmailReady(page, account, 'thread list');\n  await installGmailCapture(page, account, 'bv', 'thread list');\n\n  const rows = [];\n  const seen = new Set();\n  const pages = Math.ceil(limit / PAGE_SIZE);\n  for (let pageNumber = 1; pageNumber <= pages; pageNumber += 1) {\n    if (pageNumber === 1) {\n      await submitSearch(page, normalizedQuery, 'thread list');\n    } else {\n      const target = unwrapBrowserResult(await page.evaluate(`() => {\n        const labels = /(older|较旧|較舊|下一页|下一頁)/i;\n        const button = Array.from(document.querySelectorAll('button, [role=\"button\"]')).find((node) => {\n          const value = [node.getAttribute('aria-label'), node.getAttribute('data-tooltip'), node.getAttribute('title')]\n            .filter(Boolean).join(' ');\n          const rect = node.getBoundingClientRect();\n          return labels.test(value) && node.getAttribute('aria-disabled') !== 'true' && rect.width > 0 && rect.height > 0;\n        });","sourceCodeStart":463,"sourceCodeEnd":499,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/gmail/utils.js#L463-L499","documentation":"queryThreads() cleans the provided search string with cleanString() and throws ArgumentError when the result is empty. The library refuses to issue an empty Gmail search because Gmail's UI would either reject it or return an unfiltered inbox listing, which is almost never what the caller intended. It is a fail-fast input validation error.","triggerScenarios":"Calling queryThreads(page, '') or queryThreads(page, null); calling queryThreads(page, '   ') (whitespace-only, stripped by cleanString); passing a variable that is undefined/empty after template interpolation.","commonSituations":"Building a query string from CLI flags where the user omitted --query; concatenating filters that all evaluated to empty; a config value defaulting to '' instead of a real query.","solutions":["Pass a non-empty search query, e.g. queryThreads(page, 'from:me newer_than:7d')","Trim/validate the query at your CLI/entry point before calling queryThreads","Check that the variable holding the query is actually populated (log it before the call)"],"exampleFix":"// before\nawait queryThreads(page, opts.query);\n// after\nif (!opts.query || !opts.query.trim()) throw new Error('--query is required');\nawait queryThreads(page, opts.query.trim());","handlingStrategy":"validation","validationCode":"if (typeof query !== 'string' || !query.trim()) throw new Error('query must be a non-empty string');\nawait queryThreads(page, query.trim(), { account, limit });","typeGuard":"const isNonEmptyQuery = (q) => typeof q === 'string' && q.trim().length > 0;","tryCatchPattern":"try { await queryThreads(page, q); } catch (e) { if (e instanceof ArgumentError) { console.error('Provide a search query'); return; } throw e; }","preventionTips":["Validate CLI --query as required before invoking the library","Trim user input and reject whitespace-only strings early","Default to a broad query (e.g. 'in:anywhere') rather than empty when none given"],"tags":["validation","argument-error","gmail"],"backgroundTag":"empty-required-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}