{"record":{"id":"acaf52c1a898e92a","repo":"projectdiscovery/katana","slug":"errnocrawlingaction","errorCode":"ErrNoCrawlingAction","errorMessage":"no more actions to crawl","messagePattern":"no more actions to crawl","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"info","filePath":"pkg/engine/headless/crawler/crawler.go","lineNumber":327,"sourceCode":"\t\t\t\t\tc.logger.Debug(\"Skipping action as it is taking too long\", slog.String(\"action\", action.String()))\n\t\t\t\t\tconsecutiveFailures++\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tc.logger.Debug(\"Skipping action due to site-specific error\",\n\t\t\t\t\tslog.String(\"error\", err.Error()),\n\t\t\t\t\tslog.String(\"action\", action.String()),\n\t\t\t\t)\n\t\t\t\tconsecutiveFailures++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconsecutiveFailures = 0\n\t\t}\n\t}\n}\n\nvar ErrNoCrawlingAction = errors.New(\"no more actions to crawl\")\n\nfunc (c *Crawler) crawlFn(ctx context.Context, action *types.Action, page *browser.BrowserPage) error {\n\tdefer func() {\n\t\tc.launcher.PutBrowserToPool(page)\n\t}()\n\n\tcurrentPageHash, _, err := getPageHash(page)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.logger.Debug(\"Processing action - current state\",\n\t\tslog.String(\"current_page_hash\", currentPageHash),\n\t\tslog.String(\"action_origin_id\", action.OriginID),\n\t\tslog.String(\"action\", action.String()),\n\t)\n\n\tif action.OriginID != \"\" && action.OriginID != currentPageHash {","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/projectdiscovery/katana/blob/e3e742739c3746f085943ce918fb4e2b8daf6fe6/pkg/engine/headless/crawler/crawler.go#L309-L345","documentation":"ErrNoCrawlingAction signals that the headless crawler's crawl queue is empty and there are no navigations left, so no further actions can be executed. Crawl treats it specially: inside crawlFn it means 'stop gracefully' (returns nil) rather than a failure. It is the headless engine's way of terminating the crawl loop.","triggerScenarios":"Returned from the queue-pop path (crawler.go:419) when c.crawlQueue.Size() == 0 and no navigation actions remain; also returned by Crawl when len(navigations) == 0 and the crawl queue is empty at start.","commonSituations":"Crawl of a page with no interactive elements or links; all crawlable actions already executed or exhausted due to consecutive failures; page blocked by login/JS challenge so nothing is discoverable.","solutions":["Nothing to fix if the target genuinely has no more actions — crawl finished","Check the target in a real browser; anti-bot walls can hide all actionable elements","Ensure --headless options/proxy aren't blocking page rendering, and review hooks that may consume or skip actions"],"exampleFix":"// before: failing on any Crawl error\nif err := c.Crawl(ctx); err != nil { return err }\n// after\nif err := c.Crawl(ctx); err != nil && !errors.Is(err, crawler.ErrNoCrawlingAction) { return err }","handlingStrategy":"fallback","validationCode":"// nothing to validate pre-call; ensure the target page has actionable elements\n// e.g. verify selectors exist before crawling:\nif len(actions) == 0 && queue.Size() == 0 { skip crawl }","typeGuard":"func isNoCrawlAction(err error) bool { return errors.Is(err, crawler.ErrNoCrawlingAction) }","tryCatchPattern":"if err := c.Crawl(ctx); err != nil {\n    if errors.Is(err, crawler.ErrNoCrawlingAction) {\n        return nil // graceful end of crawl\n    }\n    return err\n}","preventionTips":["Validate the target renders interactive content (test in a real browser)","Check for anti-bot walls blocking headless rendering","Log queue size periodically to know when actions are exhausted"],"tags":["headless-browser","crawler","empty-queue"],"backgroundTag":"no-crawl-actions","analyzedSha":"e3e742739c3746f085943ce918fb4e2b8daf6fe6","analyzedAt":"2026-09-03T14:55:13.248Z","contentChangedAt":"2026-09-03T14:55:13.248Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}