Skip to content

fix: clamp setTimeout value to prevent negative timeout crash#39273

Open
veeceey wants to merge 2 commits intomicrosoft:mainfrom
veeceey:fix/issue-39166-negative-settimeout
Open

fix: clamp setTimeout value to prevent negative timeout crash#39273
veeceey wants to merge 2 commits intomicrosoft:mainfrom
veeceey:fix/issue-39166-negative-settimeout

Conversation

@veeceey
Copy link

@veeceey veeceey commented Feb 14, 2026

Problem

When a Node.js process has been running for more than ~24.8 days (2^31-1 milliseconds), monotonicTime() exceeds the max signed 32-bit integer. In raceAgainstDeadline(), the timeout is computed as:

const timeout = (deadline || kMaxDeadline) - monotonicTime();

When monotonicTime() exceeds kMaxDeadline (e.g. no explicit timeout was set, so deadline is 0 and falls back to kMaxDeadline), this produces a negative value. Node.js then coerces it to 1ms and fires the timer immediately, causing operations like browserType.connect() to fail with:

browserType.connect: Timeout undefinedms exceeded

Fix

Clamp the computed timeout to [0, kMaxDeadline]:

  • Lower bound of 0 prevents negative values that Node.js coerces to 1ms
  • Upper bound of kMaxDeadline (2^31-1) keeps it within setTimeout's valid range

How I verified this

Reproduced the issue by overriding performance.now to return a value > 2^31-1, confirming connect() fails before the fix and succeeds after.

Fixes #39166

When a Node.js process has been running for more than ~24.8 days,
monotonicTime() exceeds 2^31-1 ms. This causes the computed timeout
in raceAgainstDeadline() to become negative, which Node.js coerces
to 1ms, resulting in an immediate spurious timeout.

Clamp the timeout to [0, 2^31-1] to prevent both negative values
and values exceeding setTimeout's maximum safe delay.

Fixes microsoft#39166
new Promise<{ timedOut: true }>(resolve => {
const kMaxDeadline = 2147483647; // 2^31-1
const timeout = (deadline || kMaxDeadline) - monotonicTime();
const rawTimeout = (deadline || kMaxDeadline) - monotonicTime();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to not set a timer at all when there is no deadline, here and in other places across the code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call -- updated to skip the timer entirely when there's no deadline instead of clamping. If deadline is 0, the promise executor returns early so no setTimeout is created at all. The race then just waits on the callback.

Instead of falling back to kMaxDeadline and clamping, just return early
from the promise executor when deadline is 0. This avoids the negative
timeout issue altogether by not creating a timer when none is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: negative setTimeout in timeoutRunner causing browserType.connect() crash

2 participants