{"record":{"id":"4114e7209db8134e","repo":"apify/crawlee","slug":"the-concurrencysystem-this-autoscaledpool-borrows","errorCode":null,"errorMessage":"The ConcurrencySystem this AutoscaledPool borrows has not been started, so system load would not be monitored and the concurrency would never be adjusted. Whoever creates a ConcurrencySystem owns its lifecycle: call `await concurrencySystem.start()` before running the pools or crawlers that use it, and `await concurrencySystem.stop()` once they are all done.","messagePattern":"The ConcurrencySystem this AutoscaledPool borrows has not been started, so system load would not be monitored and the concurrency would never be adjusted\\. Whoever creates a ConcurrencySystem owns its lifecycle: call `await concurrencySystem\\.start\\(\\)` before running the pools or crawlers that use it, and `await concurrencySystem\\.stop\\(\\)` once they are all done\\.","errorType":"exception","errorClass":"CriticalError","httpStatus":null,"severity":"critical","filePath":"packages/core/src/autoscaling/autoscaled_pool.ts","lineNumber":242,"sourceCode":"     * The number of parallel tasks currently booked against the governor. When it is shared, this counts every\n     * borrowing pool's tasks, not just this one's.\n     */\n    get currentConcurrency(): number {\n        return this.#concurrencySystem.currentConcurrency;\n    }\n\n    /**\n     * Runs the auto-scaled pool. Returns a promise that gets resolved or rejected once\n     * all the tasks are finished or one of them fails.\n     *\n     * Throws if the {@apilink IConcurrencySystem|concurrency system} it borrows was never started — the pool assumes\n     * a running governor and cannot start one it does not own.\n     */\n    async run(): Promise<void> {\n        // Checked here, on an awaited path — the capacity queries inside the task loop run from intervals and\n        // `setImmediate`, where a throw would become an unhandled rejection and hang `run()` forever.\n        if (!this.#concurrencySystem.isRunning) {\n            throw new CriticalError(\n                'The ConcurrencySystem this AutoscaledPool borrows has not been started, so system load would not be ' +\n                    'monitored and the concurrency would never be adjusted. Whoever creates a ConcurrencySystem owns ' +\n                    'its lifecycle: call `await concurrencySystem.start()` before running the pools or crawlers that ' +\n                    'use it, and `await concurrencySystem.stop()` once they are all done.',\n            );\n        }\n\n        const poolPromise = new Promise((resolve, reject) => {\n            this.#resolve = resolve;\n            this.#reject = reject;\n        });\n\n        // This is here because if we scale down to let's say 1, then after each promise is finished\n        // this.maybeRunTask() doesn't trigger another one. So if that 1 instance gets stuck it results\n        // in the crawler getting stuck and even after scaling up it never triggers another promise.\n        this.#maybeRunInterval = betterSetInterval(this.maybeRunTask, this.#maybeRunIntervalMillis);\n\n        try {","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/core/src/autoscaling/autoscaled_pool.ts#L224-L260","documentation":"AutoscaledPool borrows a ConcurrencySystem owned by the caller. If `run()` is called while that system isn't running, load would never be monitored and concurrency never adjusted, so Crawlee throws a CriticalError up front (on the awaited path) instead of silently hanging.","triggerScenarios":"Manually constructing a ConcurrencySystem (or a low-level AutoscaledPool) and calling `pool.run()` without first `await concurrencySystem.start()`; or calling `stop()` on the system before the pool finishes.","commonSituations":"Custom orchestration code composing AutoscaledPool/Crawler instances directly instead of through default factories; stopping the shared concurrency system while a second pool still runs.","solutions":["Call `await concurrencySystem.start()` before `await pool.run()`.","Keep the system running until every pool/crawler using it has finished; only then `await concurrencySystem.stop()`.","Prefer high-level Crawler APIs (which own the system lifecycle) over hand-rolled AutoscaledPool wiring."],"exampleFix":"// before\nconst pool = new AutoscaledPool({ concurrencySystem: system, ... });\nawait pool.run();\n// after\nawait system.start();\nconst pool = new AutoscaledPool({ concurrencySystem: system, ... });\nawait pool.run();\nawait system.stop();","handlingStrategy":"validation","validationCode":"if (!concurrencySystem.isRunning) { throw new Error('start the ConcurrencySystem before running pools'); }\nawait pool.run();","typeGuard":null,"tryCatchPattern":"try { await pool.run(); } catch (err) { if (err instanceof CriticalError && err.message.includes('ConcurrencySystem')) { await system.start(); await pool.run(); } else { throw err; } }","preventionTips":["Always pair `system.start()` before pools and `system.stop()` after all complete","Prefer high-level Crawler classes that own the ConcurrencySystem lifecycle","In tests, assert `system.isRunning` before invoking run()"],"tags":["autoscaling","autoscaled-pool","lifecycle","core"],"backgroundTag":"resource-lifecycle-not-started","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}