D4Vinci/Scrapling · error · RuntimeError

No active crawl to stop

Error message

No active crawl to stop

What it means

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.

Source

Thrown at scrapling/spiders/spider.py:237

        """Configure sessions for this spider.

        Override this method to add custom sessions.
        The default implementation creates a FetcherSession session.

        The first session added becomes the default for `start_requests()` unless specified otherwise.

        :param manager: SessionManager to configure
        """
        from scrapling.fetchers import FetcherSession

        manager.add("default", FetcherSession())

    def pause(self):
        """Request graceful shutdown of the crawling process."""
        if self._engine:
            self._engine.request_pause()
        else:
            raise RuntimeError("No active crawl to stop")

    def _setup_signal_handler(self) -> None:
        """Set up SIGINT handler for graceful pause."""

        def handler(_signum: int, _frame: Any) -> None:
            if self._engine:
                self._engine.request_pause()
            else:
                # No engine yet, just raise KeyboardInterrupt
                raise KeyboardInterrupt

        try:
            self._original_sigint_handler = signal.signal(signal.SIGINT, handler)
        except ValueError:
            self._original_sigint_handler = None

    def _restore_signal_handler(self) -> None:
        """Restore original SIGINT handler."""

View on GitHub (pinned to 5d213a2d47)

Solutions

  1. 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)
  2. Catch RuntimeError and surface 'not running' to the user in control interfaces
  3. Use SIGINT (Ctrl+C) during start() runs — the built-in signal handler already performs the graceful pause

Example fix

// before
spider = MySpider()
spider.pause()  # nothing running yet
spider.start()

// after
spider = MySpider()
thread = Thread(target=spider.start); thread.start()
# ... later, once the crawl is running
try:
    spider.pause()
except RuntimeError:
    print("crawl not active")
Defensive patterns

Strategy: try-catch

Validate before calling

def safe_pause(spider) -> bool:
    try:
        spider.pause()
        return True
    except RuntimeError:
        return False

Try / catch

except RuntimeError:
    pass  # no active crawl; nothing to pause

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14). Data as JSON: /api/errors/4149303c010d9488. Report an issue: GitHub.