{"record":{"id":"aa45ef1f712fefd1","repo":"apify/crawlee","slug":"this-crawler-instance-is-already-running-you-can","errorCode":null,"errorMessage":"This crawler instance is already running, you can add more requests to it via `crawler.addRequests()`.","messagePattern":"This crawler instance is already running, you can add more requests to it via `crawler\\.addRequests\\(\\)`\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/basic-crawler/src/internals/basic-crawler.ts","lineNumber":1700,"sourceCode":"\n        const interval = setInterval(log, this.#statusMessageLoggingInterval * 1e3);\n        return { log, stop: () => clearInterval(interval) };\n    }\n\n    /**\n     * Runs the crawler. Returns a promise that resolves once every request has been processed and the crawler's\n     * finished-check ({@apilink BasicCrawlerOptions.taskLoopOptions|`taskLoopOptions.isFinishedFunction`}, or the\n     * default \"the request manager is empty\") reports that the crawl is over.\n     *\n     * We can use the `requests` parameter to enqueue the initial requests — it is a shortcut for\n     * running {@apilink BasicCrawler.addRequests|`crawler.addRequests()`} before {@apilink BasicCrawler.run|`crawler.run()`}.\n     *\n     * @param [requests] The requests to add.\n     * @param [options] Options for the request queue.\n     */\n    async run(requests?: TypedRequestsLike<Routes>, options?: CrawlerRunOptions): Promise<FinalStatistics> {\n        if (this.running) {\n            throw new Error(\n                'This crawler instance is already running, you can add more requests to it via `crawler.addRequests()`.',\n            );\n        }\n\n        const { purgeRequestQueue, ...addRequestsOptions } = options ?? {};\n\n        if (this.hasFinishedBefore) {\n            // When executing the run method for the second time explicitly,\n            // we need to purge the RQ to allow processing the same requests again — this is important so users can\n            // pass in failed requests back to the `crawler.run()`, otherwise they would be considered as handled and\n            // ignored — as a failed request is still handled.\n            // `purgeRequestQueue` unset purges only storage the crawler opened itself (see `#purgeableExtent`);\n            // `true` also purges a caller-supplied manager, `false` purges nothing.\n            if (purgeRequestQueue === undefined && this.#purgeableExtent === 'ambiguous') {\n                throw new Error(\n                    'Cannot decide what to purge before running again: `sameDomainDelaySecs` paces the request ' +\n                        'manager you supplied, so the per-domain queues that have to be emptied are the ' +\n                        \"crawler's while the manager underneath them is yours. Say which you want: \" +","sourceCodeStart":1682,"sourceCodeEnd":1718,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/basic-crawler/src/internals/basic-crawler.ts#L1682-L1718","documentation":"`crawler.run()` can only execute one crawl per instance at a time; `this.running` is true while a run is in progress. Calling run() again concurrently is rejected, and the message points to `crawler.addRequests()` as the supported way to feed more work into the running crawl.","triggerScenarios":"Calling `crawler.run(...)` (or awaiting two run() calls in parallel, or run() from an event handler while a run is active) while a previous `run()` on the same instance has not finished.","commonSituations":"Scheduling code that fires run() on a timer while the previous crawl is still going; calling run() inside a request handler of the same crawler; awaiting run() in two places by mistake.","solutions":["Await the current `crawler.run()` promise before calling run() again.","Use `crawler.addRequests(...)` to add URLs to the already-running crawl.","Create a new crawler instance for a concurrent crawl.","Guard with a flag/promise chain so concurrent callers wait for or reuse the active run."],"exampleFix":"// before\nsetInterval(() => crawler.run(requests), 60_000);\n// after\nasync function loop() {\n  while (true) {\n    await crawler.run(requests);\n    await new Promise((r) => setTimeout(r, 60_000));\n  }\n}","handlingStrategy":"type-guard","validationCode":"if (crawler.running) {\n  await crawler.addRequests(newRequests);\n} else {\n  await crawler.run(newRequests);\n}","typeGuard":"function canRun(c) { return typeof c === 'object' && c !== null && c.running === false; }","tryCatchPattern":"try {\n  await crawler.run(requests);\n} catch (err) {\n  if (err.message.includes('already running')) {\n    await crawler.addRequests(requests);\n  } else throw err;\n}","preventionTips":["Never call run() concurrently on the same crawler instance; serialize runs through a promise chain.","Use addRequests() to feed a running crawl instead of nested run() calls.","Do not call run() from inside request handlers of the same crawler.","Create separate instances when parallel crawls are needed."],"tags":["concurrency","lifecycle","crawler-run"],"backgroundTag":"crawler-already-running","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}