{"record":{"id":"dfe7afbe1fe9a555","repo":"headroomlabs-ai/headroom","slug":"offline-mode-headroom-binaries-offline-1-but-fet","errorCode":null,"errorMessage":"offline mode (HEADROOM_BINARIES_OFFLINE=1) but fetch required: {url}","messagePattern":"offline mode \\(HEADROOM_BINARIES_OFFLINE=1\\) but fetch required: (.+?)","errorType":"exception","errorClass":"OfflineError","httpStatus":null,"severity":"error","filePath":"headroom/binaries.py","lineNumber":241,"sourceCode":"\n\ndef _mirror_url(url: str) -> str:\n    mirror = os.environ.get(\"HEADROOM_BINARIES_MIRROR\")\n    if not mirror:\n        return url\n    # Only substitute the github.com host so that paths remain intact.\n    for prefix in (\"https://github.com\", \"https://objects.githubusercontent.com\"):\n        if url.startswith(prefix):\n            return mirror.rstrip(\"/\") + url[len(prefix) :]\n    return url\n\n\n# ---------- Download + verify --------------------------------------------- #\n\n\ndef _download(url: str, dest: Path, *, progress: bool = True) -> None:\n    if os.environ.get(\"HEADROOM_BINARIES_OFFLINE\"):\n        raise OfflineError(f\"offline mode (HEADROOM_BINARIES_OFFLINE=1) but fetch required: {url}\")\n    if not _has_writable_existing_parent(dest.parent):\n        raise OSError(f\"binary cache directory parent is not writable: {dest.parent}\")\n    dest.parent.mkdir(parents=True, exist_ok=True)\n    if not _is_writable_dir(dest.parent):\n        raise OSError(f\"binary cache directory is not writable: {dest.parent}\")\n    final_url = _mirror_url(url)\n    req = urllib.request.Request(final_url, headers={\"User-Agent\": \"headroom-binaries/1\"})\n    attempts = 3\n    for attempt in range(1, attempts + 1):\n        try:\n            with urllib.request.urlopen(req, timeout=60) as resp:  # noqa: S310 (https)\n                total = int(resp.headers.get(\"Content-Length\") or 0)\n                _stream_to(resp, dest, total, label=dest.name, show_progress=progress)\n            return\n        except urllib.error.URLError as e:\n            dest.unlink(missing_ok=True)\n            if attempt == attempts:\n                raise BinaryFetchError(","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/binaries.py#L223-L259","documentation":"_download raises OfflineError when the HEADROOM_BINARIES_OFFLINE env var is truthy-set but the requested binary is not in the local cache, forcing a network fetch. This is an intentional air-gapped-mode guard: offline mode must never silently reach the network, so a cache miss becomes a hard error naming the URL that would have been fetched.","triggerScenarios":"HEADROOM_BINARIES_OFFLINE=1 (or any non-empty value) + first use of a tool on that machine/container, or after the cache directory was cleared.","commonSituations":"Air-gapped or CI environments where offline mode is set for reproducibility but the image was never warmed with a pre-fetch step; k8s emptyDir or ephemeral containers losing the cache between runs.","solutions":["Warm the cache during image build with the same tool set, with the env var unset: RUN headroom ... (or the project's prefetch command), then set HEADROOM_BINARIES_OFFLINE=1 at runtime.","If the network is actually available, unset the variable: HEADROOM_BINARIES_OFFLINE= (or remove it from the deployment spec).","Point HEADROOM_BINARIES_MIRROR at an internal mirror and drop offline mode if direct GitHub access is the reason it was enabled."],"exampleFix":"# before (Dockerfile)\nENV HEADROOM_BINARIES_OFFLINE=1\nRUN headroom doctor  # OfflineError: cache empty\n\n# after (Dockerfile)\nRUN headroom doctor            # downloads and populates cache\nENV HEADROOM_BINARIES_OFFLINE=1","handlingStrategy":"validation","validationCode":"import os\n\ndef offline_cache_complete(urls_or_tools: list[str]) -> bool:\n    if not os.environ.get(\"HEADROOM_BINARIES_OFFLINE\"):\n        return True\n    cache = binaries_cache_dir()  # wherever the runtime caches fetched tools\n    return all((cache / t).exists() for t in urls_or_tools)\n\nassert offline_cache_complete([\"difft\", \"scc\"]), \"warm the cache before enabling offline mode\"","typeGuard":null,"tryCatchPattern":"try:\n    ensure_binary(tool)\nexcept OfflineError as e:\n    raise SystemExit(f\"offline mode set but {tool} not cached; run the prefetch step in the image build\") from e","preventionTips":["Warm the binary cache at image build time, then enable HEADROOM_BINARIES_OFFLINE at runtime.","Persist the cache in a volume so ephemeral containers do not start cold.","Add a CI job that builds the offline image and smoke-tests tool startup."],"tags":["python","binaries","offline","air-gapped","cache","environment"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}