{"record":{"id":"4df56e8d0c95830d","repo":"squidfunk/mkdocs-material","slug":"couldn-t-find-font-family-family-on-google-fon","errorCode":null,"errorMessage":"Couldn't find font family '{family}' on Google Fonts ({res.status_code}: {res.reason})","messagePattern":"Couldn't find font family '(.+?)' on Google Fonts \\((.+?): (.+?)\\)","errorType":"exception","errorClass":"PluginError","httpStatus":null,"severity":"error","filePath":"src/plugins/social/plugin.py","lineNumber":890,"sourceCode":"        # Fall back to regular font (guess if there are multiple)\n        return self._resolve_font(family, fallback)\n\n    # -------------------------------------------------------------------------\n\n    # Fetch font family from Google Fonts\n    def _fetch_font_from_google_fonts(self, family: str):\n        path = os.path.join(self.config.cache_dir, \"fonts\")\n\n        # Download manifest from Google Fonts - Google returns JSON with syntax\n        # errors, so we just treat the response as plain text and parse out all\n        # URLs to font files, as we're going to rename them anyway. This should\n        # be more resilient than trying to correct the JSON syntax.\n        url = f\"https://fonts.google.com/download/list?family={family}\"\n        res = requests.get(url)\n\n        # Ensure that the download succeeded\n        if res.status_code != 200:\n            raise PluginError(\n                f\"Couldn't find font family '{family}' on Google Fonts \"\n                f\"({res.status_code}: {res.reason})\"\n            )\n\n        # Extract font URLs from manifest\n        for match in re.findall(\n            r\"\\\"(https:(?:.*?)\\.[ot]tf)\\\"\", str(res.content)\n        ):\n            with requests.get(match) as res:\n                res.raise_for_status()\n\n                # Construct image font for analysis by directly reading the\n                # contents from the response without priorily writing to a\n                # temporary file (like we did before), as this might lead to\n                # problems on Windows machines, see https://t.ly/LiF_k\n                with BytesIO(res.content) as f:\n                    font = ImageFont.truetype(f)\n","sourceCodeStart":872,"sourceCodeEnd":908,"githubUrl":"https://github.com/squidfunk/mkdocs-material/blob/e2136532f40aed98af1f6497c0cc6a3cff9f759b/src/plugins/social/plugin.py#L872-L908","documentation":"`_fetch_font_from_google_fonts` downloads the font manifest from `https://fonts.google.com/download/list?family=...` when resolving a font family for social cards. Any non-200 response (font not found, rate limiting, network/proxy failure) raises this PluginError with the HTTP status and reason.","triggerScenarios":"A card layout requests a font family not available on Google Fonts; no network access or a blocking proxy/firewall; Google Fonts returns 403/429 (rate limit or bot protection); family name misspelled or using a non-Google-Fonts font.","commonSituations":"CI environments without internet egress; corporate proxies blocking fonts.google.com; custom `cards_font` set to a self-hosted/local font that Google doesn't serve; transient 429s during batch builds.","solutions":["Verify the font family name exists on fonts.google.com and use its exact name (spaces encoded).","For custom/local fonts, place the font files in the cache directory yourself or use a layout that references a bundled font instead of Google Fonts.","Fix network egress: configure HTTPS_PROXY, allow fonts.google.com in the firewall, or pre-populate the plugin's font cache.","Retry the build if the failure was a transient 429/5xx; add caching of downloaded fonts between CI runs."],"exampleFix":"# before (mkdocs.yml, font not on Google Fonts)\n- social:\n    cards_font: My Corporate Sans\n\n# after: use a Google-hosted family or bundle locally\n- social:\n    cards_font: Roboto","handlingStrategy":"retry","validationCode":"# preflight: fonts reachable and family known\ndef font_available(family):\n    import requests\n    r = requests.head(f\"https://fonts.google.com/download/list?family={family}\", timeout=10)\n    return r.status_code == 200","typeGuard":null,"tryCatchPattern":"from mkdocs.exceptions import PluginError\nimport time\nfor attempt in range(3):\n    try:\n        return plugin._resolve_font(family, style)\n    except PluginError as e:\n        if ' 429' in str(e) or ' 50' in str(e):\n            time.sleep(2 ** attempt)\n            continue\n        raise","preventionTips":["Cache downloaded fonts between builds (persist the plugin cache directory in CI)","Verify font families exist on fonts.google.com before adding them to layouts","Ensure CI network egress/proxy allows fonts.google.com","Use bundled/self-hosted fonts for hermetic builds"],"tags":["network","http-404","google-fonts","mkdocs-material","social-plugin"],"backgroundTag":"http-request-failed","analyzedSha":"e2136532f40aed98af1f6497c0cc6a3cff9f759b","analyzedAt":"2026-08-29T11:20:04.299Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}