The error usually means Node.js is trying to load PhantomJS code. webpage is a built-in module available to a script running under PhantomJS; it is not an npm package that Node.js can resolve. Run the script with the PhantomJS executable, or keep the handler in Node.js and use a Node-facing browser API instead. Adding a Lambda layer can package files, but it cannot change which runtime interprets your code.
Why Lambda cannot find webpage
PhantomJS has its own runtime and built-in modules. Its Web Page Module documentation shows the PhantomJS-side pattern:
var webPage = require('webpage');
var page = webPage.create();
That syntax is valid when PhantomJS interprets the file. It fails when the same file is loaded by a Node.js Lambda handler: Node resolves require() using Node’s module system, which does not include PhantomJS’s built-in webpage module. The message is therefore usually a runtime-boundary problem, not a missing dependency to install from npm.
The first diagnostic question is not “Which package should I add?” but “Which executable is running this file?” If the command, Lambda handler, or imported module runs it with Node, PhantomJS-specific calls will not work there. PhantomJS examples need to run under the PhantomJS executable; Node integrations need to use the bridge’s Node-facing API rather than requiring webpage in Node.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →#1 Best Overall
Choose the fix that matches your code
Keep the PhantomJS script and launch it as a child process
Use this route when you need to preserve existing PhantomJS script behavior. Keep PhantomJS code in its own file, pass inputs such as the URL as command-line arguments, and have the Node handler start the PhantomJS executable. Do not import the PhantomJS script into the handler with require().
The example below uses a PhantomJS script that opens a URL and prints its page title. It assumes the executable is at /opt/bin/phantomjs; set PHANTOMJS_BIN to the actual executable path in your deployment. The path is an example, not a Lambda default.
page-title.js — run by PhantomJS
var webpage = require('webpage');
var system = require('system');
var url = system.args[1];
if (!url) {
console.error('Usage: phantomjs page-title.js URL');
phantom.exit(2);
}
var page = webpage.create();
page.open(url, function (status) {
if (status !== 'success') {
console.error('Could not load URL: ' + status);
phantom.exit(1);
return;
}
console.log(page.title);
phantom.exit(0);
});
index.js — Node.js Lambda handler
const { spawn } = require('child_process');
const phantomPath = process.env.PHANTOMJS_BIN || '/opt/bin/phantomjs';
const timeoutMs = 25000;
exports.handler = async (event) => {
const url = event && event.url;
if (typeof url !== 'string' || !/^https?:///i.test(url)) {
throw new Error('Provide event.url as an http or https URL');
}
return new Promise((resolve, reject) => {
const child = spawn(phantomPath, ['/var/task/page-title.js', url], {
stdio: ['ignore', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
let settled = false;
const timer = setTimeout(() => {
child.kill('SIGKILL');
finish(new Error('PhantomJS timed out'));
}, timeoutMs);
function finish(error, result) {
if (settled) return;
settled = true;
clearTimeout(timer);
if (error) reject(error);
else resolve(result);
}
child.stdout.setEncoding('utf8');
child.stderr.setEncoding('utf8');
child.stdout.on('data', (chunk) => { stdout += chunk; });
child.stderr.on('data', (chunk) => { stderr += chunk; });
child.on('error', (error) => {
finish(new Error('Could not start PhantomJS: ' + error.message));
});
child.on('close', (code, signal) => {
if (code === 0) {
finish(null, { title: stdout.trim() });
} else {
finish(new Error(
'PhantomJS failed (exit ' + code + ', signal ' + signal + '): ' + stderr.trim()
));
}
});
});
};
This example passes the URL as a separate argument rather than constructing a shell command. That avoids shell quoting problems with URLs containing query strings or special characters. The handler collects both output streams, checks the process exit status, handles a failure to start, and sets a timeout. Adjust the timeout to fit the Lambda function’s configured timeout; allow enough time for the child process to finish before Lambda stops the invocation.
Rank #2
For a screenshot instead of a title, change the PhantomJS script to save the page using PhantomJS’s page API, and then decide how the handler will return or store the resulting file. Lambda’s writable temporary directory is /tmp; do not assume the deployment directory is writable. The right response format and storage destination depend on the function’s caller and are not determined by this module-resolution error.
Recommended Free Tools
Keep the handler in Node.js and use a Node-facing API
If the Lambda should remain a Node.js function, remove require('webpage') from every file Node loads. Use the documented API of a Node-to-PhantomJS bridge to create and control a page, or migrate to a maintained headless-browser stack. A bridge may expose a page-like object to Node, but it does not make PhantomJS built-ins available to Node’s own module resolver. Follow the specific bridge’s installation, invocation, and compatibility instructions rather than copying PhantomJS script syntax into the handler.
PhantomJS 2.1 was released on January 23, 2016 and used Qt 5.5.1/WebKit. That age is a practical reason to pin the binary and test the complete deployment on the exact Lambda runtime and architecture you use. Whether to migrate is an engineering decision based on your application’s requirements; the release date alone does not establish a particular current support policy.
Or skip the browser setup
If your actual requirement is to capture a website screenshot or PDF—not to run existing PhantomJS-specific code—ScreenshotNeo is a screenshot API and MCP server for developers. A single GET request can return an image or PDF. It does not make webpage available inside Node or repair a PhantomJS deployment; it is an alternative for the screenshot task itself.
For example, install requests in your Python environment, set your API key, then run:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
See the ScreenshotNeo API documentation for the request options. Cookie banners, popups, and chat widgets are removed before capture; bot checks, blank pages, and failed loads are not billed. An MCP server lets AI agents take screenshots, and the Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for free.
Package the Lambda files for the runtime you chose
A Lambda deployment includes the handler and any additional packages or modules it depends on. For a PhantomJS child-process design, that means deploying the Node handler, the PhantomJS script, the executable, and any required native libraries. The executable must be compatible with the function’s runtime environment and architecture. The error text by itself does not identify the correct binary build, library set, or packaging method.
Rank #4
Zip deployment
- Put the handler at the location and filename expected by the configured Lambda handler setting. In the example,
index.jsandpage-title.jsare at/var/task, the root of the extracted function package. - Install ordinary Node dependencies in the project’s
node_modulesdirectory, then create the zip with the project contents at the archive root—not an extra enclosing project folder. - Include the PhantomJS executable and its required libraries in a location your handler can access. Set
PHANTOMJS_BINto that location, and ensure the binary has executable permissions. - Build or select the native executable for the Lambda function’s architecture, such as
x86_64orarm64, and check its compatibility with the selected runtime. Test the packaged artifact, not just a local development copy.
Lambda layer
A layer is one way to distribute dependencies, not a way to switch interpreters. For Node dependencies, AWS documents layer paths such as nodejs/node_modules and runtime-specific nodejs/nodeXX/node_modules. Lambda extracts layer contents under /opt and searches documented paths. A PhantomJS executable can also be placed in a layer if packaged appropriately, but Node still cannot resolve PhantomJS’s built-in webpage module. The handler must launch the executable to run the PhantomJS script.
When ordinary Node module resolution is the issue, log process.env.NODE_PATH to inspect the search path. That can help diagnose a missing Node dependency; it will not diagnose or fix the runtime mismatch that causes Node to look for PhantomJS’s webpage built-in.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteTroubleshoot the common failure modes
| Symptom | Likely cause | What to check or change |
|---|---|---|
Cannot find module 'webpage' appears in CloudWatch logs |
Node is loading a PhantomJS script or a Node process is calling require('webpage'). |
Check the Lambda handler setting, imports, and child-process command. Run the PhantomJS file with the PhantomJS executable, or replace the call with the bridge’s Node API. |
Cannot find module 'some-package', not webpage |
A genuine Node dependency may be absent or outside Node’s search paths. | Include the dependency in the zip’s node_modules or a correctly structured layer, and inspect NODE_PATH. |
| Child process reports “could not start” or a permission error | The executable path may be wrong, the file may be missing, or executable permissions may be absent. | Verify the deployed path and POSIX permissions. Log the configured executable path and check that the binary is present in the deployed artifact. |
| Process starts but fails to load or exits unexpectedly | The executable or one of its native libraries may not match Lambda’s architecture or runtime environment. | Confirm the function architecture and test the exact packaged binary and libraries in the target environment. Capture stderr and the exit code. |
| Works locally but not in Lambda | The local binary, libraries, permissions, filesystem assumptions, or runtime may differ from the deployed configuration. | Test the complete Lambda package for the configured runtime and architecture; use a writable location such as /tmp for temporary output. |
Layer is attached but webpage still cannot be found |
The layer changes available files, not the process interpreting the script. | Keep webpage inside the PhantomJS script and invoke that script under PhantomJS; do not require it from Node. |
| Function invocation runs out of time | The page load or child process exceeded its timeout budget. | Set a bounded child-process timeout, ensure the Lambda timeout is longer than the child-process limit, and handle the timeout as a controlled invocation failure. |
Check performance, reliability, and migration before keeping PhantomJS
Starting a native child process adds work to each invocation, and page navigation time varies with the target site and network conditions. Reusing a warm Lambda environment may avoid some setup work elsewhere in your handler, but it should not be treated as proof that a particular PhantomJS binary, page, or browser process is reusable safely. Measure the complete invocation with your actual package, target pages, and concurrency rather than assuming a fixed capture time.
Best Value
For reliability, return a controlled error when the executable cannot start, the page fails, or the process times out. Keep stderr and exit status in logs without treating a successful Node invocation as proof the PhantomJS page loaded successfully. Limit child-process execution to the Lambda time budget, and avoid relying on persistent local files between invocations.
PhantomJS 2.1’s 2016 release makes it a legacy choice for new work. If retaining it, pin the binary and validate the full Lambda package whenever you change the function runtime, architecture, or binary. If your project can change, compare the work of rewriting PhantomJS calls around a Node-facing page API with the packaging and compatibility requirements of a maintained browser solution. The right option depends on whether preserving the old script or reducing long-term runtime risk matters more.
Keep the runtime boundary explicit
For existing PhantomJS code, the direct fix is to launch the script with PhantomJS and treat it as a separate process. For a Node-only handler, remove the PhantomJS module import and use a documented Node-facing page API or a replacement browser stack. Package files and native binaries for Lambda’s configured runtime and architecture; a layer alone cannot make Node understand PhantomJS built-ins.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

