{"record":{"id":"849b7a34e9f0f21b","repo":"ocrmypdf/OCRmyPDF","slug":"at-least-one-provider-is-required","errorCode":null,"errorMessage":"At least one provider is required","messagePattern":"At least one provider is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/ocrmypdf/font/font_provider.py","lineNumber":179,"sourceCode":"\n\nclass ChainedFontProvider:\n    \"\"\"Font provider that tries multiple providers in order.\n\n    This allows combining builtin fonts with system fonts, trying\n    the builtin provider first and falling back to system fonts\n    for fonts not bundled with the package.\n    \"\"\"\n\n    def __init__(self, providers: list[FontProvider]):\n        \"\"\"Initialize chained font provider.\n\n        Args:\n            providers: List of font providers to try in order.\n                       The first provider that returns a font wins.\n        \"\"\"\n        if not providers:\n            raise ValueError(\"At least one provider is required\")\n        self.providers = providers\n\n    def get_font(self, font_name: str) -> FontManager | None:\n        \"\"\"Get a FontManager for the named font.\n\n        Tries each provider in order until one returns a font.\n\n        Args:\n            font_name: Logical font name (e.g., 'NotoSans-Regular')\n\n        Returns:\n            FontManager if any provider has the font, None otherwise\n        \"\"\"\n        for provider in self.providers:\n            if font := provider.get_font(font_name):\n                return font\n        return None\n","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/ocrmypdf/OCRmyPDF/blob/5074a0b0e109362422b768fd271ed84bf717c4ec/src/ocrmypdf/font/font_provider.py#L161-L197","documentation":"The font resolution chain (FontProviderChain-like class) tries providers in order; constructing it with an empty providers list is rejected immediately with ValueError('At least one provider is required').","triggerScenarios":"new FontProviderChain([]) — typically when a caller builds the provider list conditionally and every branch returned nothing (e.g. system fonts disabled and builtin provider skipped).","commonSituations":"Feature-flag combinations that filter out all providers; upstream code changes returning an empty list where one provider was expected.","solutions":["Ensure at least BuiltinFontProvider is included in the providers list.","Fix the logic producing the empty list (check flags/conditions used to filter providers)."],"exampleFix":"# before\nproviders = [p for p in candidates if p]\nchain = FontProviderChain(providers)  # may be empty\n# after\nproviders = [p for p in candidates if p] or [BuiltinFontProvider()]\nchain = FontProviderChain(providers)","handlingStrategy":"validation","validationCode":"assert providers, 'at least one font provider required'","typeGuard":"def has_provider(providers: list) -> bool:\n    return len(providers) > 0","tryCatchPattern":null,"preventionTips":["Default provider lists to [BuiltinFontProvider()] when filtering yields nothing.","Unit-test provider construction paths for all flag combinations."],"tags":["ocrmypdf","fonts","provider-chain","validation"],"backgroundTag":"empty-provider-list","analyzedSha":"5074a0b0e109362422b768fd271ed84bf717c4ec","analyzedAt":"2026-08-27T11:57:38.523Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}