{"record":{"id":"7c0419dd44a53bdb","repo":"D4Vinci/Scrapling","slug":"no-active-crawl-use-this-property-inside-async-f","errorCode":null,"errorMessage":"No active crawl. Use this property inside `async for item in spider.stream():`","messagePattern":"No active crawl\\. Use this property inside `async for item in spider\\.stream\\(\\):`","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"scrapling/spiders/spider.py","lineNumber":330,"sourceCode":"        token = set_logger(self.logger)\n        try:\n            self._engine = CrawlerEngine(self, self._session_manager, self.crawldir, self._interval)\n            async for item in self._engine:\n                yield item\n        finally:\n            self._engine = None\n            reset_logger(token)\n            if self.log_file:\n                for handler in self.logger.handlers:\n                    if isinstance(handler, logging.FileHandler):\n                        handler.close()\n\n    @property\n    def stats(self) -> CrawlStats:\n        \"\"\"Access current crawl stats (works during streaming).\"\"\"\n        if self._engine:\n            return self._engine.stats\n        raise RuntimeError(\"No active crawl. Use this property inside `async for item in spider.stream():`\")\n","sourceCodeStart":312,"sourceCodeEnd":331,"githubUrl":"https://github.com/D4Vinci/Scrapling/blob/5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f/scrapling/spiders/spider.py#L312-L331","documentation":"The Spider.stats property returns the live CrawlStats only while self._engine exists (i.e., during an active crawl). Outside `async for item in spider.stream()` — before starting, after the loop ends, or after start() returned — the engine is set to None and the property raises RuntimeError.","triggerScenarios":"Accessing spider.stats before calling start()/stream(); reading it after the stream generator is exhausted or the loop breaks (the finally block in stream() sets _engine = None); logging stats in a finally/after clause outside the iteration.","commonSituations":"Trying to print final stats after the crawl loop — the correct result source is the CrawlResult returned by start(), not spider.stats; accessing stats in cleanup code after an exception ended the stream.","solutions":["Inside the stream loop, read spider.stats while iterating (e.g. every N items)","For final stats with start(), capture the returned CrawlResult: result = spider.start(); result.stats","Move any post-crawl stats reporting to use the CrawlResult/CrawlStats object instead of the spider property"],"exampleFix":"// before\nasync for item in spider.stream():\n    ...\nprint(spider.stats)  # engine already cleared -> RuntimeError\n\n// after\nresult = spider.start()\nprint(result.stats)","handlingStrategy":"try-catch","validationCode":"stats = spider.stats if spider._engine else None  # read only during the stream loop","typeGuard":"def crawl_active(spider) -> bool:\n    return spider._engine is not None","tryCatchPattern":"try:\n    stats = spider.stats\nexcept RuntimeError:\n    stats = None  # crawl not running","preventionTips":["Read spider.stats only inside `async for item in spider.stream():`","Use the CrawlResult returned by start() for final stats","Copy stats into your own variable before ending the stream"],"tags":["stats","lifecycle","streaming"],"backgroundTag":null,"analyzedSha":"5d213a2d4764002bfc4fed33c32fe09fa8b0bf7f","analyzedAt":"2026-08-14T22:23:09.440Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}