Back to Knowledge Hub
Fingerprint

JavaScript Stack Depth: A Browser Privacy Signal

Learn why JavaScript recursion limits vary across contexts, how to review the signal responsibly, and what BotBrowser can control.

BotBrowser Team

Documentation

Prefer the maintained product doc?

This article has a matching page in the docs center. Use the docs for the canonical setup flow, current flags, and long-term reference.

JavaScript recursion has a practical limit. When a function keeps calling itself, the browser eventually stops the calls and reports a stack overflow, commonly as a RangeError. The number of successful calls before that error is observable application behavior. It is not defined as one universal value by the web platform, so it can differ between browser implementations, releases, operating systems, and execution contexts.

That difference is relevant to privacy because a page can perform a small recursive test and retain the resulting count as one input to a broader browser fingerprint. The count is not a name, account credential, or proof of where someone is located. It is a low-volume technical signal that may add a little separation when combined with many other observations. Treating it as one signal keeps the discussion proportional and avoids turning a compatibility detail into an identity claim.

The useful question for a product team is not how to collect a richer profile. It is how to keep application behavior predictable when the value changes, and how to choose a browser configuration that is consistent with the profile a team is reviewing. A fallback-first application should continue when a recursion test is unavailable, inconclusive, or different from an earlier session. No user-facing task should depend on a particular depth number.

A browser recursion check is reviewed across contexts with a fallback-first application policy

What the measurement represents

Each active JavaScript call adds a frame to a call stack. A frame holds return information and execution state, while the browser manages the memory and safety checks around that stack. Frame shape matters: a tiny recursive function can reach a different count from a function with more parameters or local values. A measurement therefore describes the chosen test as well as the context in which it ran. Comparing counts is meaningful only when the test shape and surrounding page state are kept stable.

The eventual error protects the page from unbounded recursion. Catching the error lets an application finish its check, but catching it does not move the limit. JavaScript cannot request a larger native stack for an existing thread, and it cannot rewrite the browser's stack bookkeeping. A script can decide not to run the check, yet that choice does not change what another page could observe in its own context.

Operating-system stack allocation is one contributor, but it is not the only contributor. Browser implementations use different frame layouts, safety margins, optimization paths, and context setup. A browser update can alter any of these details. A host with plenty of free memory can still report a different recursion count from another host because the limit is a runtime policy, not a live measurement of available memory.

The main window and a worker do not necessarily start with identical stack conditions. Dedicated workers and shared workers have their own setup, and WebAssembly code follows its own execution rules. A comparison should therefore record the context name and the test shape. A result from the main thread should not be presented as a promise about a worker, and a worker result should not be used as a device classification.

Why context and release matter

A page may observe a stable value during one browser session, yet stability is not permanence. Browser releases, operating-system updates, security hardening, and changes to page code can affect the count. Small changes in a recursive function can also change it. This is why a historical value is weak evidence on its own and why a support team should record the browser release and test revision when investigating a compatibility issue.

The signal is most useful as a consistency check inside an owned test. For example, a team can compare the main-thread result with the result from a worker that its application already uses. The test can then answer whether the selected profile behaves as expected for that application. It should not try to identify an unknown visitor, infer a person's hardware, or make an access decision from the count.

A privacy-respecting service keeps the result close to the decision that needs it. If the application only needs to know whether a recursion-heavy optional feature is safe, it can use a local yes-or-no policy and discard the raw count. If a support case needs more detail, record the test revision, browser release, and selected policy rather than adding the number to an account identifier. This makes the value easier to delete and harder to reuse as a cross-session label.

The same boundary applies to analytics. A dashboard can report that a feature used a baseline or reduced path without sorting people by stack depth. Aggregated outcomes such as completion, cancellation, and recovery tell the product team more about the experience than a long-lived inventory of technical values. Keep required content and controls available when the measurement is missing or fails.

BotBrowser support boundary

BotBrowser exposes --bot-stack-seed to choose how the observable JavaScript recursion limit is derived for a profile. The profile value uses the stack-depth value associated with the selected profile. The real value keeps the native host result. An integer seed requests deterministic variation for a session, so repeating the same profile and seed can provide a repeatable review input. These modes describe the supported configuration choices; they are not a promise that every application will return one fixed number.

The setting is intended to keep the browser-facing signal aligned with the profile being reviewed. It can apply the selected behavior to the main thread, Web Workers, and WebAssembly contexts supported by the product. Teams should still measure each context used by their own application because a worker and a main window have different execution conditions. A profile choice is not a substitute for a compatibility test, and a test result is not proof of a person's identity.

BotBrowser does not change the operating system's native stack allocation or remove general engine memory constraints. The selected behavior concerns the recursion limit that a page can observe; it does not redesign the application's ordinary call-stack usage. Typical page code should remain well below the limit, but an application that intentionally performs deep recursion still needs its own performance, error handling, and resource tests.

BotBrowser also cannot know whether a third-party page chooses to run a recursion check. It can provide the configured profile behavior, but it cannot make a page forget the value, decide how that page stores it, or replace the site's privacy policy. The appropriate product response is to minimize collection, explain optional diagnostics, and keep a useful baseline path when a value is absent.

Choosing a mode for a review

Use profile when the goal is to review an application against the stack behavior recorded for a particular profile. This is useful when the team is checking that related browser surfaces remain coherent during a regression review. Keep the browser release, profile, and test revision recorded together so a later comparison can distinguish a configuration change from a page change.

Use real when the test is meant to describe the host environment that is actually running the browser. This mode is appropriate for ordinary application debugging, capacity work, or a compatibility report that intentionally represents the current machine. It does not make the result portable to another host, and it does not say that the host is representative of every supported environment.

Use an integer seed when a repeatable variation is needed for separate review inputs without selecting a hand-written depth. A seed is an input to the browser configuration, not a value that a page should expose as an identifier. Keep the seed with the profile and release in a controlled test record, and avoid placing it in user accounts, URLs, or analytics fields. Different seeds may produce different results, so compare only runs that use the same stated inputs.

Do not choose a mode to force an application through an arbitrary number. An extreme or undocumented value can make the recursion signal disagree with other profile surfaces and can hide a real application defect. The safer review question is whether the selected profile and release produce a plausible, repeatable result for the contexts the application owns. If a deep-recursion workload needs more room, validate that workload separately with the native mode and ordinary resource monitoring.

A repeatable application review

Start with a small fixture that calls a bounded recursive function and catches the expected overflow. Record only the count needed for the review, the context in which the fixture ran, and the browser release. Run the fixture in the main window first, then in each worker context that the application actually uses. The fixture should not enumerate unrelated browser properties or send a technical inventory to a server.

Repeat the fixture with the same profile and seed after a clean browser start. A repeatable result is useful evidence that the configuration was loaded consistently, but it is not a production guarantee. If the result changes, check the profile path, release, worker setup, and fixture revision before drawing a conclusion. A changed page bundle can alter frame shape even when the browser configuration did not change.

Review the failure path as carefully as the success path. If recursion is unavailable, the test throws an unexpected error, or a worker cannot start, the application should keep required content and provide an understandable fallback. Optional enhancements can be deferred or disabled, while navigation, account controls, and accessible status information remain usable. This behavior is more important to a person than the raw depth count.

For cross-host review, compare declared inputs before comparing results. The same profile name is not enough if the browser release, feature flags, fixture code, or context differs. Keep the record short: profile identifier, mode, seed type, release, context, selected application policy, and outcome. Do not retain a detailed hardware description simply because the test happened to expose one technical value.

The browser fingerprinting overview explains why many small signals can be combined. The deterministic browser behavior guide covers repeatable profile decisions, while navigator property guidance shows why a reported browser property should remain separate from an identity claim. These links provide context; they do not turn stack depth into a standalone identifier.

Limits and privacy decisions

Stack depth has limited entropy and should be treated as supporting evidence. It can vary with code shape and runtime conditions, and it does not reliably identify a browser, device, or person by itself. A service that stores it indefinitely may create more privacy risk than product value. Prefer a short-lived policy decision, a documented retention period, and deletion when the review or support case ends.

Do not use a recursion count as authorization, fraud proof, location evidence, or a substitute for account security. It cannot tell an application whether a visitor is trustworthy. It also cannot establish that two sessions belong to the same person. Authentication, rate controls, and abuse handling need their own server-side evidence and should not depend on an incidental JavaScript limit.

Do not promise identical behavior across every browser family, operating system, worker implementation, or WebAssembly runtime. Public documentation can explain the expected scope, while compatibility tests establish what a particular product supports. When a release changes the observed count, update the test record and investigate user-visible outcomes before changing a profile policy.

A clear support note should state both sides of the boundary: BotBrowser can select profile, native, or seeded stack-depth behavior for supported contexts, but it cannot modify host allocation, guarantee an application result, or control what a third-party page records. This wording keeps the product claim verifiable and gives operators a practical next step: test the owned workflow, preserve its fallback, and retain only the evidence needed for that decision.

Describe a changed recursion result as a compatibility observation, not as a new identity category. State which browser release, profile mode, context, and fixture revision were compared. If the page still completes its task, no user-facing change may be needed. If an optional recursive feature becomes unreliable, change that feature's policy and recovery path rather than broadening collection or assigning a label to the visitor. A useful record also names the visible fallback, the user action that remained available, and the retention period for the review data. This keeps the incident understandable to support staff who did not run the fixture. It also gives application owners a concrete acceptance point: the page either completed the requested task or explained the bounded fallback. A raw number without that context cannot show whether the product worked.

Support teams can make this review easier by separating configuration evidence from application evidence. Configuration evidence says which profile, mode, seed type, and browser release were selected. Application evidence says whether the page loaded, whether the recursive workload completed, and whether the fallback remained available. Keeping these records separate prevents a technical count from becoming a hidden account attribute and makes a later investigation reproducible without customer data.

The test fixture should also exercise cancellation and navigation. A recursive operation that is no longer needed should stop when the user leaves the view, and a worker that is closed should not keep scheduling work. These checks are ordinary lifecycle tests, but they prevent a stack-depth experiment from becoming a source of unnecessary resource use. They also make the fallback meaningful because the page can abandon optional work without blocking its required controls.

Accessibility belongs in the same review. A status message for an unavailable worker or optional enhancement should be exposed to assistive technology, and the fallback should preserve keyboard access, readable text, and the same essential action. A technical measurement is not a substitute for an accessible experience. When a team records only the selected policy and visible outcome, it can improve these states without retaining the underlying recursion count.

Before changing a profile policy, compare several ordinary causes for a difference: a browser release, a worker startup path, a changed recursive function, a security setting, or a host configuration. The count alone cannot distinguish these causes. A short checklist with a named fixture and a bounded fallback is more reliable than repeated sampling. Once the owned application behaves correctly, stop collecting the value and keep only the decision record required by the support process.

The review record should have a clear expiry. Keep the profile, release, fixture revision, context, and visible outcome only as long as the compatibility question remains open. Remove the raw count when it no longer informs a decision, and make the fallback behavior part of the acceptance note. This keeps a technical observation tied to a short-lived engineering task instead of allowing it to become an account attribute.

These practices give stack depth a narrow, defensible role. It can help a team review whether a selected browser profile behaves coherently in the contexts it owns, while application design remains responsible for resilience, privacy, and accessibility. The result is a testable product boundary: configure the browser deliberately, measure only what the review needs, and keep the user experience complete when the signal changes.

Public sources

#Stack-Depth#Fingerprinting#Recursion#Javascript#Privacy

Take BotBrowser from research to production

The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.