• /
  • EnglishEspañolFrançais日本語한국어Português
  • Inicia sesiónComenzar ahora

Troubleshoot runtime upgrade errors

Node.js 22 upgrade issues

The following issues are specific to upgrading monitors to Node.js 22.

Unhandled open handles

Symptom: Monitor fails during validation or execution Cause: Node.js 22 is stricter about process lifecycles

The most common issue when upgrading to Node.js 22 is unhandled open handles. If your monitor scripts contain unhandled open handles (such as unresolved promises, lingering timers, or unclosed network connections), your monitors will fail.

Common types of open handles include:

  • Unresolved promises: Ensure all promises are properly awaited or handled
  • Lingering timers: Clear all setTimeout and setInterval calls
  • Unclosed connections: Close all network connections, database connections, and file handles
  • HTTP/HTTPS connections: Properly consume response data and close connections

HTTP/HTTPS connections remain open

Symptom: Monitor hangs or times out Cause: Response data not consumed or keepAlive enabled

Solution A: Consume response data

To use http or https, listen to the data event. Otherwise, the HTTPS connection remains open.

const https = require('https');
https.get('https://example.com', (res) => {
console.log('Status Code:', res.statusCode);
// Consume the response data
res.on('data', (d) => {
process.stdout.write(d);
});
}).on('error', (e) => {
console.error('Error:', e);
});

Solution B: Disable keepAlive

Set the keepAlive option to false to close HTTP connections after the request completes.

const https = require('https');
const { Agent } = require('https'); // or http for HTTP requests
const options = {
hostname: 'example.com',
path: '/',
method: 'GET',
agent: new Agent({ keepAlive: false }), // Disable keepAlive
};
const req = https.request(options, (res) => {
console.log(`Status: ${res.statusCode}`);
res.on('data', (d) => {
process.stdout.write(d);
});
});
req.on('error', (e) => {
console.error(e);
});
req.end();

Stream resources not cleaned up

Symptom: Resource leaks or hanging monitors Cause: Streams not destroyed in finally blocks

Solution

Clean up streams in a finally block to ensure proper resource disposal.

const got = require('got');
let downloadStream = got.stream("https://example.com/test.txt");
try {
// Your stream processing logic here
} finally {
if (downloadStream) {
downloadStream.destroy();
}
}

Sockets not properly destroyed

Symptom: Monitor validation fails with "open handles" error Cause: Socket cleanup missing in success code paths

Destroy sockets properly in all code paths.

Problematic code

// ❌ Problematic code - Socket not closed in success case
const req = https.request(options, (res) => {
try {
const cert = res.socket.getPeerCertificate();
if (cert && Object.keys(cert).length > 0) {
const validTo = cert.valid_to || cert.validTo;
console.log("Raw certificate valid_to:", validTo);
resolve({ validTo: validTo });
} else {
reject(new Error("Could not get certificate information"));
}
} catch (err) {
res.destroy();
if (res.socket) res.socket.destroy();
reject(err);
}
});

Add cleanup functionality for all code paths:

// ✅ Better approach - Proper cleanup in all cases
const req = https.request(options, (res) => {
try {
const cert = res.socket.getPeerCertificate();
// Always destroy the response and socket
const cleanup = () => {
try {
res.destroy();
if (res.socket && !res.socket.destroyed) {
res.socket.destroy();
}
} catch (e) {
console.log("Cleanup warning:", e.message);
}
};
if (cert && Object.keys(cert).length > 0) {
const validTo = cert.valid_to || cert.validTo;
console.log("Raw certificate valid_to:", validTo);
cleanup();
resolve({ validTo: validTo });
} else {
cleanup();
reject(new Error("Could not get certificate information"));
}
} catch (err) {
res.destroy();
if (res.socket) res.socket.destroy();
reject(err);
}
});

Timers not cleared

Symptom: Monitor doesn't complete execution Cause: setTimeout/setInterval not cleared

Problematic code

// ❌ Problematic code
setTimeout(() => {
console.log('This might cause issues');
}, 5000);

Clear the timer when done:

// ✅ Better approach
const timerId = setTimeout(() => {
console.log('This is properly handled');
}, 1000);
// Clear the timer when done
clearTimeout(timerId);

Unresolved promises

Symptom: Script appears to finish but validation fails Cause: Promises not properly awaited or handled

Solution: Ensure all promises have .then(), .catch(), or are awaited.

Chrome runtime upgrade issues

The following issues are specific to upgrading scripted browser monitors to newer Chrome runtimes.

Scripted browser: Attempts to interact with elements fail

Symptom: findElement and other methods fail in new runtime but work in legacy runtime Cause: Promise handling differences between Selenium WebDriver versions

When validating a monitor created in an older runtime against the Chrome 100 (or newer) runtime, findElement and other methods to find and interact with elements on the page may fail due to promise handling differences. If the monitor passes in a legacy runtime, fails in the new runtime, and the element is present in the screenshot, improve your promise handling logic.

Selenium WebDriver promise manager and control flow allowed some functions to execute in order in legacy runtimes, without managing promises. This capability was removed in Selenium WebDriver 4.0 and is no longer available in the runtime. All async functions and promises need to be managed with await or with .then promise chains. This will ensure script functions execute in the expected order.

Problematic code

Promise manager and control flow could allow this partial script to complete successfully, even though $browser.get returns a promise and the promise is not being handled correctly:

// ❌ Problematic code - Promises not handled
$browser.get('http://example.com');
$browser.findElement($driver.By.css('h1'));

In the Chrome 100 (or newer) runtime, any methods that return a promise need to use await or .then syntax to properly sequence steps. Using await is recommended due to cleaner syntax and easier usage, but .then promise chains are still supported too.

// ✅ Better approach - Using await
await $browser.get('http://example.com');
let el = await $browser.findElement($driver.By.css('h1'));

Scripted browser: Deprecation warnings ($browser and $driver)

Symptom: Deprecation warnings appear in monitor logs Cause: Using older node browser runtime image

Deprecation warnings for $browser and $driver were removed starting with the 2.0.29 or newer version of the browser runtime. You no longer receive these warnings when using public locations. Update your node browser runtime image if you receive these warnings when using private locations.

Scripted browser: waitForAndFindElement and waitForPendingRequests

Symptom: Methods not available when using $selenium and $webDriver Cause: New Relic custom methods not included in Selenium WebDriver 4.1 base APIs

The waitForAndFindElement and waitForPendingRequests methods are New Relic custom methods that are provided in the Chrome 72 and older scripted browser runtimes. They can still be used with $driver and $browser in the Chrome 100 and newer runtimes, but are not available when using the Selenium WebDriver 4.1 APIs directly with $selenium and $webDriver. This change better aligns New Relic's Selenium WebDriver implementation with the base Selenium WebDriver implementation.

To continue using waitForAndFindElement or waitForPendingRequests in the new runtime, paste code examples into your monitors.

API runtime upgrade issues

The following issues are specific to upgrading scripted API monitors to newer Node.js runtimes.

Scripted API: Differences between request and got

Symptom: API scripts using request module features fail in new runtime Cause: Migration from deprecated request module to got module

The Node.js 10 and older scripted API runtimes used the request Node.js module to provide a $http object that could be used to test APIs.

The Node.js 16 and newer scripted API runtimes use got instead of request. The request module was deprecated in 2020 and is no longer included in new API or browser based runtimes. The $http object provides a custom request-like experience while being powered by got to provide backward compatibility for basic use cases while still avoiding the use of a deprecated module. Not all advanced use cases of request are supported. There are script examples and a conversion guide available.

Sugerencia

The $http object also returns the request-like experience for any customers attempting to use request directly in Node.js 16 and newer scripted API runtimes.

Scripted API: Unexpected token JSON.parse errors

Symptom: Unexpected token errors when using JSON.parse Cause: Response body already parsed automatically when content-type is application/json

Attempting to use the JSON.parse function while interacting with the response body produces unexpected token errors in scripted API monitors using the Node.js 16 and newer runtime. If the content-type response header is application/json, the $http object returns parsed JSON in the response body. Additional calls attempting to use JSON.parse to parse the response body fail with this error because the response body has already been parsed.

If the content-type response header is not application/json, the response body is not automatically parsed and you must use the JSON.parse function.

Scripted API: HEAD or GET request cannot be used with a body

Symptom: HTTP HEAD or GET requests fail with body-related errors Cause: New runtime enforces HTTP specification that prohibits bodies in HEAD/GET requests

You cannot include a request body with a HTTP HEAD or GET request. The request module used by the Node 10 and older runtime allowed this, but this will cause errors in the new runtime. The most common suggestions include:

  • Do not include a body in your request, even if it's empty
  • Avoid unnecessary options on your HEAD or GET request, like json: true

Scripted API: Query string (qs) differences

Symptom: Query string options not working in new runtime Cause: Option name changed from qs: to searchParams:

In the Node 10 or older runtimes, query string configurations were passed using the qs: option. For the Node 16 runtime, use the searchParams: option instead. Only the name of the option needs to change. The content of the query string does not need to be updated.

Symptom: Cookie handling with jar: true not working Cause: Cookie jar implementation changed in Node 16 runtime

In the Node 10 or older runtimes you could use the option jar: true to store cookies in a cookie jar between requests.

In the Node 16 runtime you must create a cookie jar using the tough-cookie module and then refer to that cookie jar in your request instead. If you created a cookie jar named cookies, refer to it in your options as cookieJar: cookies

Scripted API: Form differences

Symptom: Form-based requests fail or behave differently Cause: Differences between request and got modules for form handling

Due to differences between the request module used for the $http object in Node 10 and older runtimes and the got module that's used for the $http object in the Node 16 runtime, you may encounter issues with requests using forms in API monitors.

If so, use the form-data module to create and include your form with your request as shown in this partial example:

const FormData = require('form-data');
let form = new FormData();
form.set('fieldName1','value1');
form.set('fieldName2','value2');
let req = {
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'multipart/form-data',
},
body: form
}

UUID module version differences

Symptom: Module import errors with uuid module Cause: Updated uuid module syntax in Node 16 runtime

The Node 16 runtime includes a newer version of the uuid module that forces the use of updated require syntax.

Node 10 and older: const uuid = require('uuid');

Node 16 (assuming use of uuidv4): const { v4: uuidv4 } = require('uuid');

Copyright © 2026 New Relic Inc.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.