{"record":{"id":"2aeca5c3aa447fca","repo":"headroomlabs-ai/headroom","slug":"invalid-url-url","errorCode":null,"errorMessage":"Invalid URL: {url}","messagePattern":"Invalid URL: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"headroom/graph/installer.py","lineNumber":73,"sourceCode":"\ndef download_cbm(version: str | None = None) -> Path:\n    \"\"\"Download codebase-memory-mcp binary from GitHub releases.\n\n    Returns path to installed binary.\n    \"\"\"\n    version = version or CBM_VERSION\n    plat = _detect_platform()\n    filename = f\"codebase-memory-mcp-{plat}.tar.gz\"\n    url = f\"{GITHUB_RELEASE_URL}/{version}/{filename}\"\n\n    CBM_BIN_DIR.mkdir(parents=True, exist_ok=True)\n    target_path = CBM_BIN_DIR / CBM_BIN_NAME\n\n    logger.info(\"Downloading codebase-memory-mcp %s for %s ...\", version, plat)\n\n    try:\n        if not url.startswith((\"http://\", \"https://\")):\n            raise ValueError(f\"Invalid URL: {url}\")\n\n        with urlopen(url, timeout=60) as response:  # noqa: S310\n            data = response.read()\n    except Exception as e:\n        raise RuntimeError(f\"Failed to download codebase-memory-mcp from {url}: {e}\") from e\n\n    # Extract binary from tar.gz\n    try:\n        with tarfile.open(fileobj=io.BytesIO(data), mode=\"r:gz\") as tar:\n            for member in tar.getmembers():\n                if member.name.endswith(CBM_BIN_NAME) or member.name == CBM_BIN_NAME:\n                    member.name = target_path.name\n                    tar.extract(member, CBM_BIN_DIR)\n                    break\n            else:\n                raise RuntimeError(\"codebase-memory-mcp binary not found in archive\")\n    except tarfile.TarError as e:\n        raise RuntimeError(f\"Failed to extract archive: {e}\") from e","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/graph/installer.py#L55-L91","documentation":"Raised inside install_cbm's download try-block when the constructed release URL does not start with http:// or https://. Because the URL is always built from the GITHUB_RELEASE_URL constant plus a version string, this guard only fires if the constant is overridden/corrupted or the version passed in contains characters that break the scheme (e.g. a version like 'file:///x' or a malformed custom base). In practice it is a defensive invariant, converted to RuntimeError by the enclosing except.","triggerScenarios":"Passing an exotic version string (e.g. '../../local' or one containing whitespace/newlines) to install_cbm, or monkeypatching/patching GITHUB_RELEASE_URL in tests or site config to a non-http value. Normal invocations with tags like 'v1.2.3' cannot hit it.","commonSituations":"Corporate mirrors that patch the release URL constant to a file:// or internal scheme; tests faking the downloader; accidentally passing a full URL as the version argument.","solutions":["Pass a plain release tag as version (e.g. 'v0.4.2') and leave URL construction to the function.","If you must mirror, override GITHUB_RELEASE_URL with a full https:// URL, not file:// or a bare host.","Check for accidental double-prefixing: version should not itself contain 'https://'."],"exampleFix":"# before\ninstall_cbm(version=\"https://github.com/.../v1.0.0\")  # mangled URL -> Invalid URL\n\n# after\ninstall_cbm(version=\"v1.0.0\")","handlingStrategy":"validation","validationCode":"def _is_http_url(u: str) -> bool:\n    return isinstance(u, str) and u.startswith((\"http://\", \"https://\"))\n\nassert _is_http_url(url), f\"refusing non-http url: {url!r}\"","typeGuard":null,"tryCatchPattern":"try:\n    install_cbm(version=ver)\nexcept RuntimeError as e:\n    if \"Invalid URL\" in str(e):\n        raise ValueError(f\"bad version/url input: {ver!r}\") from e\n    raise","preventionTips":["Pass release tags (vX.Y.Z), never full URLs, as the version argument.","Keep URL overrides to https:// constants; reject other schemes at config-load time.","Treat hitting this error as a signal your inputs/constants are malformed, not a network problem."],"tags":["url-validation","installer","defensive-check","configuration"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}