{"record":{"id":"2733e0e258d984fa","repo":"lissy93/web-check","slug":"no-url-specified","errorCode":null,"errorMessage":"No URL specified","messagePattern":"No URL specified","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"api/_common/middleware.js","lineNumber":62,"sourceCode":"    return new Promise((_, reject) => {\n      setTimeout(() => {\n        reject(new Error(`Request timed-out after ${timeoutMs} ms`));\n      }, timeoutMs);\n    });\n  };\n\n  // Vercel\n  const vercelHandler = async (request, response) => {\n    const queryParams = request.query || {};\n    const rawUrl = queryParams.url;\n\n    const { skip, reason } = shouldSkip(request.url, rawUrl);\n    if (skip) {\n      return response.status(200).json({ skipped: reason });\n    }\n\n    if (!rawUrl) {\n      return response.status(500).json({ error: 'No URL specified' });\n    }\n\n    try {\n      const url = normalizeUrl(rawUrl);\n      const result = await Promise.race([handler(url, request), createTimeoutPromise(TIMEOUT)]);\n      response.status(200).json(typeof result === 'object' ? result : JSON.parse(result));\n    } catch (error) {\n      const isTimeout = error.message.includes('timed-out') || response.statusCode === 504;\n      const message = isTimeout ? `${error.message}\\n\\n${timeoutErrorMsg}` : error.message;\n      response.status(isTimeout ? 408 : 500).json({ error: message });\n    }\n  };\n\n  // Netlify\n  const netlifyHandler = async (event, context) => {\n    const queryParams = event.queryStringParameters || event.query || {};\n    const rawUrl = queryParams.url;\n","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/lissy93/web-check/blob/af1a97759fc8bcc43c876c94f2ccb018ce215f90/api/_common/middleware.js#L44-L80","documentation":"The shared Vercel middleware extracts the raw url from the request, applies shouldSkip, and if rawUrl is falsy responds 500 with { error: 'No URL specified' }. Unlike sibling handlers this is a returned HTTP 500 response, not a thrown exception, and only fires when shouldSkip did not already short-circuit.","triggerScenarios":"Any request to an endpoint wrapped by this middleware that carries no url query parameter and does not match the skip rules.","commonSituations":"Curl/browser probes hitting the endpoint root without ?url=, monitoring health checks that ping the path bare, or client code reading the wrong query key before calling.","solutions":["Always include ?url=<target> in requests to middleware-wrapped endpoints","For uptime monitors, use a dedicated health path or add the endpoint to skip rules rather than passing no url","Client-side, validate presence of the param before issuing the request"],"exampleFix":"// before\nfetch('/api/status'); // 500 { error: 'No URL specified' }\n\n// after\nif (!targetUrl) throw new Error('target url required');\nfetch(`/api/status?url=${encodeURIComponent(targetUrl)}`);","handlingStrategy":"validation","validationCode":"const url = new URL(request.url, 'http://x').searchParams.get('url');\nif (!url) return badRequest('url query parameter is required');\nconst out = await fetch(endpoint + '?url=' + encodeURIComponent(url));","typeGuard":"const hasUrlQueryParam = (u) => new URL(u, 'http://x').searchParams.has('url');","tryCatchPattern":"const r = await fetch(ep);\nif (r.status === 500) { const b = await r.json(); if (b.error === 'No URL specified') return badRequest('missing ?url='); }\nthrow new Error(`unexpected ${r.status}`);","preventionTips":["Always append ?url= to middleware-wrapped endpoints, even for manual probes","Point health checks at a dedicated health path, not business endpoints","Consider returning 400 instead of 500 for client-caused missing params"],"tags":["middleware","query-parameter","api","validation"],"backgroundTag":"missing-required-parameter","analyzedSha":"af1a97759fc8bcc43c876c94f2ccb018ce215f90","analyzedAt":"2026-08-27T11:44:27.410Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}