From 10922d114fb5118566dacff22a797b928b766559 Mon Sep 17 00:00:00 2001 From: Santosh-Prasad-Verma Date: Sun, 16 Aug 2026 03:30:35 +0530 Subject: [PATCH] fix(maxun-core): prevent orphaned crawl loops after timeout Move isAborted flag reset from cleanup() to run() so that in-flight crawl loops see the abort flag during teardown and exit cleanly, preventing CPU/RAM leaks from timed-out runs. Previously, cleanup() reset isAborted = false during teardown, which accidentally un-aborted active async crawl loops. The loops would then continue running against a closed Playwright browser context, consuming CPU/RAM and flooding server logs with errors. Now, isAborted is only reset at the start of a new run() call, which is the only time it should be cleared. This covers all three timeout triggers: task-runner, scheduler, and API record handler. Refs: #1183 --- maxun-core/src/interpret.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 055a7004..c2528f74 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -3223,6 +3223,7 @@ export default class Interpreter extends EventEmitter { */ public async run(page: Page, params?: ParamType): Promise { this.log('Starting the workflow.', Level.LOG); + this.isAborted = false; const context = page.context(); page.setDefaultNavigationTimeout(100000); @@ -3294,8 +3295,9 @@ export default class Interpreter extends EventEmitter { this.namedResults = {}; this.serializableDataByType = { scrapeList: {}, scrapeSchema: {}, crawl: {}, search: {} }; - // Reset state - this.isAborted = false; + // Reset state (isAborted is intentionally NOT reset here — it must + // remain true so that in-flight crawl loops see the abort flag and exit. + // It is reset at the start of run() instead.) this.initializedWorkflow = null; this.log('Interpreter cleanup completed', Level.DEBUG);