{"record":{"id":"5818b3bf82ef59f3","repo":"usestrix/strix","slug":"invalid-url-url","errorCode":null,"errorMessage":"Invalid URL: {url}","messagePattern":"Invalid URL: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"strix/tools/proxy/caido_api.py","lineNumber":182,"sourceCode":"    # \"Field required\" on the missing raw field. Always request both —\n    # the caller picks which one to surface via ``part``.\n    opts = RequestGetOptions(request_raw=True, response_raw=True)\n    return await client.request.get(request_id, opts)\n\n\n_FRAMING_HEADERS = frozenset({\"content-length\", \"transfer-encoding\"})\n\n\ndef build_raw_request(\n    *,\n    method: str,\n    url: str,\n    headers: dict[str, str],\n    body: str,\n) -> tuple[ConnectionInfoInput, bytes]:\n    parsed = urlparse(url)\n    if not parsed.scheme or not parsed.netloc:\n        raise ValueError(f\"Invalid URL: {url}\")\n    is_tls = parsed.scheme.lower() == \"https\"\n    host = parsed.hostname or \"\"\n    port = parsed.port or (443 if is_tls else 80)\n    path = parsed.path or \"/\"\n    if parsed.query:\n        path = f\"{path}?{parsed.query}\"\n\n    final_headers = {**headers}\n    final_headers.setdefault(\"Host\", parsed.netloc)\n    final_headers.setdefault(\n        \"User-Agent\",\n        \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 \"\n        \"(KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36\",\n    )\n    # Framing headers inherited from the captured request describe the ORIGINAL\n    # body; once the body is modified for replay they are stale. We always send a\n    # plain (non-chunked) body with an explicit Content-Length, so drop any\n    # inherited Content-Length AND Transfer-Encoding (case-insensitively) and","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/tools/proxy/caido_api.py#L164-L200","documentation":"build_raw_request() converts a logical request (method/url/headers/body) into a Caido ConnectionInfoInput plus raw bytes. It urlparse()s the URL and requires both a scheme and a netloc; otherwise ValueError. This catches malformed replay targets before any socket is opened.","triggerScenarios":"Calling repeat_request (or build_raw_request directly) with a URL like '/api/foo' (no scheme/host), 'example.com/x' (no scheme), or 'http://' (no host). Usually the URL comes from full_url_from_components() reconstructing it from a captured raw request whose Host header or absolute-URI request line is missing.","commonSituations":"Replaying a captured request whose original form was origin-form (path-only) and no Host header survived; LLM-generated modification sets url to a relative path; proxy captures with mangled request lines.","solutions":["Pass modifications with an explicit absolute URL including scheme and host when replaying origin-form requests.","Ensure the captured request retains a Host header so full_url_from_components can reconstruct the absolute URL.","Validate the URL with urlparse before calling repeat_request and fix or skip malformed ones.","If targeting a different host, set both the url and the Host header consistently in modifications."],"exampleFix":"# before\nawait repeat_request(req_id, modifications={\"url\": \"/api/login\", ...})\n\n# after\nawait repeat_request(req_id, modifications={\"url\": \"https://target.example/api/login\", ...})","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef is_absolute_http_url(url: str) -> bool:\n    p = urlparse(url)\n    return p.scheme in {\"http\", \"https\"} and bool(p.netloc)\n\nif not is_absolute_http_url(mods.get(\"url\", \"\")):\n    mods[\"url\"] = f\"https://{host_from_host_header}{path}\"  # absolutize before replay","typeGuard":"def is_replayable_url(url: object) -> bool:\n    if not isinstance(url, str):\n        return False\n    p = urlparse(url)\n    return p.scheme in {\"http\", \"https\"} and bool(p.netloc)","tryCatchPattern":"try:\n    await repeat_request(request_id, modifications=mods)\nexcept ValueError as exc:\n    if \"Invalid URL\" in str(exc):\n        continue  # skip malformed replay target, log the id\n    raise","preventionTips":["Always pass an absolute URL (scheme + host) in replay modifications.","Preserve Host headers when capturing so absolute URLs can be reconstructed.","Validate modification payloads from LLM agents before replaying them."],"tags":["proxy","caido","url","replay","validation"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}