{"record":{"id":"b128a66c33d19ef6","repo":"jackwener/OpenCLI","slug":"zlibrary-search-query-cannot-be-empty","errorCode":null,"errorMessage":"zlibrary search query cannot be empty","messagePattern":"zlibrary search query cannot be empty","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/zlibrary/utils.js","lineNumber":38,"sourceCode":"    throw new ArgumentError('Z-Library book URL must be a valid http(s) URL', `Example: ${ZLIBRARY_ORIGIN}/book/...`);\n  }\n  if (!['http:', 'https:'].includes(url.protocol) || !ZLIBRARY_ALLOWED_HOSTS.has(url.hostname)) {\n    throw new ArgumentError(\n      `Unsupported Z-Library URL host: ${url.hostname}`,\n      `Pass a book URL under ${ZLIBRARY_DOMAIN}, for example ${ZLIBRARY_ORIGIN}/book/...`,\n    );\n  }\n  return url.toString();\n}\n\n/**\n * Build a Z-Library search URL.\n * Z-Library uses /s/<url-encoded-query> for search.\n */\nexport function buildSearchUrl(query) {\n  const normalized = String(query || '').trim();\n  if (!normalized) {\n    throw new ArgumentError('zlibrary search query cannot be empty');\n  }\n  return `${ZLIBRARY_ORIGIN}/s/${encodeURIComponent(normalized)}`;\n}\n\n/**\n * Extract book title from page context.\n * Tries z-bookcard shadow DOM first, then falls back to page title.\n */\nexport async function extractBookTitle(page) {\n  const title = await page.evaluate(`\n      (() => {\n        const card = document.querySelector('z-bookcard');\n        if (card && card.shadowRoot) {\n          const el = card.shadowRoot.querySelector('[class*=\"title\"], h1, a');\n          if (el) return el.textContent.trim().split('\\\\n')[0].trim();\n        }\n        return document.title.replace(/\\\\s*[-|].*$/, '').trim();\n      })()","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/zlibrary/utils.js#L20-L56","documentation":"buildSearchUrl throws ArgumentError (code ARGUMENT, exit 2) when the search query is empty or whitespace-only after trimming. The library refuses to build a /s/ search URL for an empty query because Z-Library would return an unusable page.","triggerScenarios":"Calling `zlibrary search` with an empty string, only spaces, an unquoted shell variable that expanded to nothing, or passing null/undefined programmatically.","commonSituations":"Missing shell quoting (`$QUERY` unset), CI pipelines where a previous step produced an empty term, or programmatic calls with an uninitialized variable.","solutions":["Provide a non-empty search term: `zlibrary search \"keyword\"`","Quote shell variables and default them (`\"${QUERY:?query required\"}`) so empty values fail loudly","Validate the query in your script before calling the CLI"],"exampleFix":"// before\nconst q = process.env.QUERY; // ''\nawait zlibrarySearch({ query: q });\n// after\nconst q = process.env.QUERY;\nif (!q || !q.trim()) throw new Error('QUERY is required');\nawait zlibrarySearch({ query: q });","handlingStrategy":"validation","validationCode":"if (typeof query !== 'string' || !query.trim()) {\n  throw new Error('zlibrary search query must be a non-empty string');\n}","typeGuard":null,"tryCatchPattern":"try {\n  results = await zlibrarySearch({ query });\n} catch (e) {\n  if (e?.code === 'ARGUMENT' && /cannot be empty/.test(e.message)) {\n    // supply a real query\n  }\n  throw e;\n}","preventionTips":["Quote shell variables: zlibrary search \"$QUERY\"","Fail fast on unset variables with ${VAR:?message}","Validate query non-empty in wrapper scripts before invoking"],"tags":["argument-validation","empty-input"],"backgroundTag":"empty-search-query","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}