{"record":{"id":"ea1761c4bf1871a3","repo":"lissy93/web-check","slug":"url-provided-is-invalid","errorCode":null,"errorMessage":"URL provided is invalid","messagePattern":"URL provided is invalid","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"api/screenshot.js","lineNumber":77,"sourceCode":"    await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });\n    await page.evaluate(() => {\n      if (!document.querySelector('body')) {\n        throw new Error('No body element found on the page');\n      }\n    });\n    const buffer = await page.screenshot();\n    return buffer.toString('base64');\n  } finally {\n    if (browser) await browser.close().catch(() => {});\n  }\n};\n\nconst screenshotHandler = async (targetUrl) => {\n  if (!targetUrl) throw new Error('URL is missing from queryStringParameters');\n  try {\n    new URL(targetUrl);\n  } catch {\n    throw new Error('URL provided is invalid');\n  }\n\n  log.debug(`request received: ${targetUrl}`);\n  try {\n    return { image: await directChromiumScreenshot(targetUrl) };\n  } catch (directError) {\n    log.warn(`direct chromium failed, falling back to puppeteer: ${directError.message}`);\n  }\n  try {\n    return { image: await puppeteerScreenshot(targetUrl) };\n  } catch (error) {\n    if (/ENOENT|Browser was not found|Could not find Chromium/i.test(error.message)) {\n      return { skipped: error.message };\n    }\n    log.error(`puppeteer screenshot failed: ${error.message}`);\n    throw error;\n  }\n};","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/lissy93/web-check/blob/af1a97759fc8bcc43c876c94f2ccb018ce215f90/api/screenshot.js#L59-L95","documentation":"screenshotHandler passes the supplied URL straight to the URL constructor (no scheme prepending). If it throws — missing protocol, invalid characters, malformed host — this error surfaces. Note the stricter contract compared to sibling endpoints: here the scheme is mandatory.","triggerScenarios":"Calling with 'example.com' (no https://), 'htp://example.com', URLs containing spaces or invalid percent-encoding, or 'https://' with empty host.","commonSituations":"Users omitting the protocol because other endpoints auto-prepend it, mobile keyboards autocorrecting '://' , or unencoded input copied from chat clients.","solutions":["Include the full scheme: pass https://example.com not example.com","encodeURIComponent the URL when building the query string client-side","Pre-validate with new URL(candidate) in the caller before hitting the endpoint"],"exampleFix":"// before\nfetch(`/api/screenshot?url=${'example.com'}`); // 500: URL provided is invalid\n\n// after\nconst target = 'example.com'.startsWith('http') ? 'example.com' : `https://${'example.com'}`;\nfetch(`/api/screenshot?url=${encodeURIComponent(target)}`);","handlingStrategy":"type-guard","validationCode":"const isValidAbsoluteUrl = (s) => { try { const u = new URL(s); return u.protocol === 'http:' || u.protocol === 'https:'; } catch { return false; } };\nif (!isValidAbsoluteUrl(url)) return badRequest('url must be absolute with http(s) scheme');","typeGuard":"const isHttpUrl = (v) => {\n  if (typeof v !== 'string') return false;\n  try { const u = new URL(v); return /^https?:$/.test(u.protocol); } catch { return false; }\n};","tryCatchPattern":"try { await screenshotHandler(url); }\ncatch (e) {\n  if (e.message === 'URL provided is invalid') return badRequest('include the scheme, e.g. https://example.com');\n  throw e;\n}","preventionTips":["Normalise to absolute https:// URLs client-side before sending","encodeURIComponent the url when building query strings","Prefer a single shared URL-normalisation helper instead of per-endpoint variants with different strictness"],"tags":["validation","url-parsing","screenshot","api"],"backgroundTag":"invalid-url-format","analyzedSha":"af1a97759fc8bcc43c876c94f2ccb018ce215f90","analyzedAt":"2026-08-27T11:44:27.410Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}