fix(TE-23055): bound page/context teardown so a crashed target cannot hang the web scanner build - #546
Open
shrinishLT wants to merge 1 commit into
Open
fix(TE-23055): bound page/context teardown so a crashed target cannot hang the web scanner build#546shrinishLT wants to merge 1 commit into
shrinishLT wants to merge 1 commit into
Conversation
… hang the build On rate-limited/WAF-protected sites the target can crash mid-capture (page.goto: Page crashed / setViewportSize: Target crashed). page.close()/context.close() then never resolve on the dead target, so the capture promise never settles, Promise.allSettled never returns and finalizeBuild is unreachable — the build stays 'running' until the job times out (seen as ~30 minute scans). Evidence from an instrumented run: 12 'closing page/context' vs 11 'page/context closed'. Race the teardown against BROWSER_CLOSE_TIMEOUT (15s) and continue if it does not return; closeBrowsers() force-kills the browser process afterwards. Scope: this fixes only the hang, so the build completes quickly. Screenshots may still show Access Denied after the first few captures — that is the customer's WAF rate limiting and is resolved only once the HYE VMs are whitelisted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parthlambdatest
approved these changes
Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Web scanner runs against
jobs.prudential.comnever finished — the build stayedrunninguntil the job timed out (reported as "scan taking ~30 minutes"), and nofinalizeBuildwas ever sent.Root cause
The site is behind Imperva and rate-limits the scan. Once limiting kicks in, the WAF serves a challenge/redirect that destroys the page's execution context mid-scroll, and the browser target then crashes:
All viewports fail, so
captureScreenshotsForConfigthrows — and itsfinallyrunsawait page.close(); await context.close(). On a crashed target those never resolve. The capture promise is stranded, soPromise.allSettledincaptureScreenshotsAsyncnever settles,captureScreenshotsnever returns, andfinalizeBuildis unreachable.Evidence from an instrumented run: 12 ×
closing page/contextvs 11 ×page/context closed— exactly one teardown never came back.Note
exitOnError: falseon the capture task does not help here: it rescues a task that throws, not one that hangs.Fix
Race the teardown against
BROWSER_CLOSE_TIMEOUT(15s) and continue if it doesn't return. Abandoning a wedged page/context is safe becausecloseBrowsers()force-kills the browser process immediately afterwards.screenshot.ts— bounded teardown in thefinally, with a warning when it times outconstants.ts—BROWSER_CLOSE_TIMEOUT: 15000The happy path is unchanged: a healthy close resolves in milliseconds, so no warning and no behavioural difference.
How
BROWSER_CLOSE_TIMEOUTwas chosenThe instinct is to use the maximum of the waits we already have. That's the wrong rule here, because every existing wait bounds page work, whereas this bounds teardown of a page we're already finished with.
Existing waits/timeouts in the capture path
waitForPageRender/DEFAULT_PAGE_LOAD_TIMEOUTpage.goto— network + renderwaitForTimeout(config / per-URL)scrollToBottomAndBackToTopscrollTime(8) ≈ 800 mssmoothScrollToBottom(lazy load)maxScrolls50 ×scrollDelay300 ≈ 15,000 mshumanLikeScrollwaitForDiscoveryMeasured cost of a healthy close (fully-loaded, fully-scrolled page at 1920×1080)
page.close()context.close()Playwright doesn't run
beforeunloadon close (runBeforeUnloaddefaults tofalse), doesn't wait for in-flight network, and capture enables no video/HAR/trace recording — artifact flush being the only thing that legitimately makescontext.close()slow. So a healthy close is ≤30 ms, three orders of magnitude under the chosen value.Taking the max (180,000 ms) would mean 6 concurrent browser configs each waiting 3 minutes on a target already known dead — up to 3 extra minutes of dead wait per run, on exactly the runs this PR is meant to speed up. The value isn't "how long might this legitimately take" (≈30 ms) but "how long before we conclude the target is dead". 15,000 ms is ~500× the worst measured close and matches the largest in-page wait in the system. 5,000 ms would also be defensible; 15,000 ms leaves margin for a loaded CI host running six browsers.
Caveats: measurements are macOS/unloaded (Linux CI will be slower, by a small multiple — not 500×); and if video/HAR/trace recording is ever enabled for capture, this constant must be revisited.
Scope — what this does and does not fix
Does fix: the hang. The build now finalizes and completes quickly instead of running until the job timeout.
Does not fix: the screenshots themselves. On rate-limited sites the first few captures succeed and subsequent ones still return Access Denied block pages. That is the customer's WAF rate limiting, and it can only be resolved once the HYE VMs are whitelisted by the customer.
Verification
close().completed(verified on stage webscanner runs, and on two back-to-back A/B runs).RFC: LambdatestIncPrivate/internal-docs#2624
🤖 Generated with Claude Code