{"record":{"id":"4149303c010d9488","repo":"D4Vinci/Scrapling","slug":"no-active-crawl-to-stop","errorCode":null,"errorMessage":"No active crawl to stop","messagePattern":"No active crawl to stop","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"scrapling/spiders/spider.py","lineNumber":237,"sourceCode":"        \"\"\"Configure sessions for this spider.\n\n        Override this method to add custom sessions.\n        The default implementation creates a FetcherSession session.\n\n        The first session added becomes the default for `start_requests()` unless specified otherwise.\n\n        :param manager: SessionManager to configure\n        \"\"\"\n        from scrapling.fetchers import FetcherSession\n\n        manager.add(\"default\", FetcherSession())\n\n    def pause(self):\n        \"\"\"Request graceful shutdown of the crawling process.\"\"\"\n        if self._engine:\n            self._engine.request_pause()\n        else:\n            raise RuntimeError(\"No active crawl to stop\")\n\n    def _setup_signal_handler(self) -> None:\n        \"\"\"Set up SIGINT handler for graceful pause.\"\"\"\n\n        def handler(_signum: int, _frame: Any) -> None:\n            if self._engine:\n                self._engine.request_pause()\n            else:\n                # No engine yet, just raise KeyboardInterrupt\n                raise KeyboardInterrupt\n\n        try:\n            self._original_sigint_handler = signal.signal(signal.SIGINT, handler)\n        except ValueError:\n            self._original_sigint_handler = None\n\n    def _restore_signal_handler(self) -> None:\n        \"\"\"Restore original SIGINT handler.\"\"\"","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/spiders/spider.py#L219-L255","documentation":"Spider.pause() forwards a graceful-pause request to the internal CrawlerEngine, which only exists while a crawl is running. Calling pause() before start()/stream() (or after the crawl finished and _engine was reset to None) raises RuntimeError.","triggerScenarios":"Calling spider.pause() before spider.start() or before entering the `async for` over spider.stream(); calling it after the crawl completed; calling it from another thread before the engine was constructed.","commonSituations":"Wiring pause into a UI/CLI that can be clicked before the crawl starts; pausing in a finally block after the crawl already ended; race between a control thread and crawl startup.","solutions":["Only call pause() while a crawl is active; guard with the same condition the library uses (check the private engine or track crawl state yourself)","Catch RuntimeError and surface 'not running' to the user in control interfaces","Use SIGINT (Ctrl+C) during start() runs — the built-in signal handler already performs the graceful pause"],"exampleFix":"// before\nspider = MySpider()\nspider.pause()  # nothing running yet\nspider.start()\n\n// after\nspider = MySpider()\nthread = Thread(target=spider.start); thread.start()\n# ... later, once the crawl is running\ntry:\n    spider.pause()\nexcept RuntimeError:\n    print(\"crawl not active\")","handlingStrategy":"try-catch","validationCode":"def safe_pause(spider) -> bool:\n    try:\n        spider.pause()\n        return True\n    except RuntimeError:\n        return False","typeGuard":null,"tryCatchPattern":"except RuntimeError:\n    pass  # no active crawl; nothing to pause","preventionTips":["Track crawl lifecycle in the controlling code and only enable pause controls while running","Rely on the built-in SIGINT handler during start() runs"],"tags":["lifecycle","pause-resume","runtime-state"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}