{"record":{"id":"adb204336c2a3ad2","repo":"unclecode/crawl4ai","slug":"max-links-must-be-positive","errorCode":null,"errorMessage":"max_links must be positive","messagePattern":"max_links must be positive","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"crawl4ai/async_configs.py","lineNumber":1203,"sourceCode":"        \"\"\"\n        self.include_internal = include_internal\n        self.include_external = include_external\n        self.include_patterns = include_patterns\n        self.exclude_patterns = exclude_patterns\n        self.concurrency = concurrency\n        self.timeout = timeout\n        self.max_links = max_links\n        self.query = query\n        self.score_threshold = score_threshold\n        self.verbose = verbose\n        \n        # Validation\n        if concurrency <= 0:\n            raise ValueError(\"concurrency must be positive\")\n        if timeout <= 0:\n            raise ValueError(\"timeout must be positive\")\n        if max_links <= 0:\n            raise ValueError(\"max_links must be positive\")\n        if score_threshold is not None and not (0.0 <= score_threshold <= 1.0):\n            raise ValueError(\"score_threshold must be between 0.0 and 1.0\")\n        if not include_internal and not include_external:\n            raise ValueError(\"At least one of include_internal or include_external must be True\")\n    \n    @staticmethod\n    def from_dict(config_dict: Dict[str, Any]) -> \"LinkPreviewConfig\":\n        \"\"\"Create LinkPreviewConfig from dictionary (for backward compatibility).\"\"\"\n        if not config_dict:\n            return None\n        \n        return LinkPreviewConfig(\n            include_internal=config_dict.get(\"include_internal\", True),\n            include_external=config_dict.get(\"include_external\", False),\n            include_patterns=config_dict.get(\"include_patterns\"),\n            exclude_patterns=config_dict.get(\"exclude_patterns\"),\n            concurrency=config_dict.get(\"concurrency\", 10),\n            timeout=config_dict.get(\"timeout\", 5),","sourceCodeStart":1185,"sourceCodeEnd":1221,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/crawl4ai/async_configs.py#L1185-L1221","documentation":"LinkPreviewConfig requires max_links > 0 because it caps how many discovered links get previewed. Zero or negative max_links would make the feature a no-op with ambiguous intent, so it fails fast instead of silently skipping work.","triggerScenarios":"LinkPreviewConfig(max_links=0) or negative; computing max_links as total_budget - links_already_used and getting <= 0.","commonSituations":"Trying to disable link previews via max_links=0; dynamic budgets in scrapers that allocate zero links to some pages.","solutions":["Use a positive cap, e.g. LinkPreviewConfig(max_links=10)","To disable previews entirely, pass link_preview_config=None in CrawlerRunConfig","Clamp computed budgets: max(1, budget)"],"exampleFix":"// before\ncfg = LinkPreviewConfig(max_links=0)  # attempt to disable\n// after\nrun_cfg = CrawlerRunConfig(link_preview_config=None)  # actual way to disable","handlingStrategy":"validation","validationCode":"if not previews_wanted:\n    run_cfg = CrawlerRunConfig(link_preview_config=None)\nelse:\n    run_cfg = CrawlerRunConfig(link_preview_config=LinkPreviewConfig(max_links=max(1, budget)))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use link_preview_config=None to disable, never max_links=0","Clamp computed link budgets to >= 1"],"tags":["validation","configuration","link-preview"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}