How to Fix Socket Hang Up with chrome-aws-lambda on Lambda
Fix Puppeteer’s Lambda “socket hang up” by aligning Chromium versions, raising memory, isolating /tmp, and checking VPC networking.
Short answer: When chromium.puppeteer.launch() fails with Error: socket hang up on Lambda, Puppeteer usually lost its localhost Chrome DevTools connection while the Chromium process was starting. It does not prove that the target website rejected the request. Start by pairing compatible chrome-aws-lambda and puppeteer-core releases, using the package’s launch settings, giving Lambda at least 512 MB (1600 MB or more is recommended), isolating and cleaning /tmp, and then checking VPC routing if the function is VPC-connected.
The exact pattern is documented in chrome-aws-lambda issue #207, opened April 1, 2021: code worked locally but failed during chromium.puppeteer.launch on Lambda. Use the sequence below to identify which part of your deployment is failing.
1. Confirm which phase is failing
Separate a launch-time disconnect from a navigation-time failure before changing code. Add version and phase logging around the browser startup:
const chromium = require('chrome-aws-lambda');
exports.handler = async (event) => {
console.log(JSON.stringify({
node: process.version,
arch: process.arch,
chromeAwsLambda: require('chrome-aws-lambda/package.json').version,
puppeteer: chromium.puppeteer?.version || 'unknown',
chromiumRevision: chromium.revision || 'unknown',
memoryMb: process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE,
tmp: process.env.AWS_LAMBDA_FUNCTION_NAME
}));
let browser;
try {
console.log('phase=launch');
browser = await chromium.puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath,
headless: chromium.headless,
ignoreHTTPSErrors: true
});
console.log('phase=launch-complete');
const page = await browser.newPage();
console.log('phase=navigation');
await page.goto(event.url || 'https://example.com', {
waitUntil: 'domcontentloaded',
timeout: 30000
});
console.log('phase=navigation-complete');
return await page.title();
} finally {
if (browser) await browser.close();
}
};
If the last log is phase=launch, investigate the local Chromium process, package compatibility, memory, executable path, and temporary storage first. If launch completes and the error appears during page.goto, investigate DNS, NAT, security groups, target-site TLS, timeouts, and the page itself. Puppeteer’s troubleshooting guide also documents browser-startup failures and points to maintained Chromium packages for Lambda environments: Puppeteer troubleshooting.
2. Align chrome-aws-lambda and Puppeteer versions
chrome-aws-lambda is versioned against specific Puppeteer minor versions and Chromium revisions. Installing arbitrary versions independently can leave Puppeteer speaking to a browser revision it does not support. Use the package repository’s version table and install the matching puppeteer-core release. The legacy table includes chrome-aws-lambda 10.1 with Puppeteer 10.1 and Chromium revision 884014 (Chrome 92.0.4512.0).
Inspect the versions in the deployed artifact
npm ls chrome-aws-lambda puppeteer puppeteer-core
node -p "require('chrome-aws-lambda/package.json').version"
node -p "require('puppeteer-core/package.json').version"
Do not mix a new Puppeteer major with an old chrome-aws-lambda binary. Pin both dependencies in package.json and deploy the lockfile used to produce them.
When the legacy package is the wrong fit
The repository is a legacy compatibility target. If your Lambda runtime, architecture, or Puppeteer version is newer than its table, test a maintained Chromium package such as @sparticuz/chromium or use a Lambda container image. Keep the browser package and automation library pinned as a pair. Migration does not fix every network or memory problem, but it avoids asking an old binary to support an unrelated Puppeteer release.
3. Use the documented launch shape
Start with the values supplied by chrome-aws-lambda. They select Lambda-compatible flags, viewport defaults, the extracted executable, and headless mode. Add flags only after logs identify a concrete sandbox, shared-memory, GPU, or process issue.
const chromium = require('chrome-aws-lambda');
exports.handler = async (event) => {
let browser;
try {
browser = await chromium.puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath,
headless: chromium.headless,
ignoreHTTPSErrors: true
});
const page = await browser.newPage();
await page.goto(event.url || 'https://example.com', {
waitUntil: 'domcontentloaded',
timeout: 30000
});
return {
statusCode: 200,
body: JSON.stringify({ title: await page.title() })
};
} finally {
if (browser) await browser.close();
}
};
Keep ignoreHTTPSErrors only when your application requires it. It is not a general fix for a launch disconnect. The finally block matters because a browser left open can consume memory and file descriptors in a reused execution environment.
4. Raise Lambda memory and inspect process exits
Allocate at least 512 MB. The project recommends 1600 MB or more. Lambda memory also controls CPU allocation, so a low setting can make Chromium startup unreliable even when the process technically fits in memory.
- Set the function memory to 1600 MB for an initial diagnostic deployment.
- Record configured memory, duration, timeout, and whether the invocation was cold or warm.
- Inspect CloudWatch logs for Chromium stderr, exit codes, and a timeout immediately before the disconnect.
- After the function is stable, reduce memory only while measuring startup time and failure rate.
A browser killed during startup can surface to Puppeteer as a WebSocket reset. A timeout that is close to the function limit can produce a later navigation error that looks similar, so compare timestamps with the Lambda timeout.
5. Isolate and clean /tmp
Lambda’s /tmp directory is disposable, but it can persist across warm invocations in the same execution environment. If you need a profile, use a unique directory and remove it when the browser closes. If logs show accumulated profiles or core dumps, clean stale files before launch.
const fs = require('fs/promises');
const os = require('os');
const path = require('path');
const crypto = require('crypto');
const chromium = require('chrome-aws-lambda');
exports.handler = async (event) => {
const profile = path.join(
os.tmpdir(),
`chrome-${crypto.randomUUID()}`
);
let browser;
try {
await fs.mkdir(profile, { recursive: true });
browser = await chromium.puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath,
headless: chromium.headless,
userDataDir: profile,
ignoreHTTPSErrors: true
});
const page = await browser.newPage();
await page.goto(event.url || 'https://example.com', {
waitUntil: 'domcontentloaded',
timeout: 30000
});
return await page.title();
} finally {
if (browser) await browser.close();
await fs.rm(profile, { recursive: true, force: true });
}
};
Do not assume an old /tmp/puppeteer_data directory is the universal cause. Puppeteer issue #3927 reports browser disconnections during roughly 500 near-simultaneous invocations and shows a persistent temporary directory; treat that as a reason to investigate storage, profile reuse, and concurrency.
6. Check VPC networking separately
A launch-time localhost WebSocket error points first to the local Chromium process. Networking can still create adjacent failures when the function is VPC-connected or the page immediately makes outbound requests.
A VPC-connected Lambda sends outbound traffic through the VPC. For internet access, the function’s private subnet normally needs a route to a NAT gateway in a public subnet. Verify:
- Private-subnet route tables contain a default route to the NAT gateway.
- The NAT gateway is in a public subnet with an internet gateway route.
- Security groups allow the required egress.
- Network ACLs allow return traffic; AWS notes that ephemeral ports 1024–65535 may be needed for intermittent TCP/UDP traffic.
- DNS support and hostnames are enabled for the VPC.
- The function role, ENI permissions, subnet IP capacity, and ENI quotas are sufficient.
Use the AWS references for the exact topology: Lambda networking troubleshooting and NAT gateways. Test a known public URL from the function before diagnosing the target site.
7. Troubleshooting checklist
| Symptom | Likely cause | Fix |
|---|---|---|
Socket hang up during launch() |
Chromium exited before Puppeteer connected | Align versions, use package defaults, raise memory, inspect stderr and exit code |
| Works locally, fails only in Lambda | Incompatible binary, missing Lambda flags, low memory, or runtime architecture mismatch | Pin a matching pair and deploy a Lambda-compatible Chromium build |
ENOENT for executable |
Binary was not packaged or extracted path is wrong | Use await chromium.executablePath; inspect the deployed artifact |
| Browser disconnects after many warm invocations | Leaked browser processes, stale profiles, or temporary-storage accumulation | Always close in finally, isolate userDataDir, clean stale /tmp data |
| Navigation timeout or DNS error | VPC route, NAT, DNS, security group, NACL, or target-site delay | Test outbound connectivity and verify routes and ephemeral ports |
| Fails near the Lambda timeout | Insufficient timeout or slow cold start/navigation | Increase timeout, wait for domcontentloaded, and measure cold versus warm runs |
| TLS certificate error | Target certificate chain or strict HTTPS validation | Fix the target certificate; use ignoreHTTPSErrors only when required |
| Intermittent failures under concurrency | Memory, CPU, ENI, NAT, or temporary-storage contention | Load-test gradually, cap concurrency, and inspect CloudWatch and VPC metrics |
8. A repeatable diagnostic sequence
- Capture the exact phase and versions. Log Node.js runtime, architecture, package versions, Chromium revision, memory, timeout, and the last completed phase.
- Align the dependency pair. Use the
chrome-aws-lambdarepository’s compatibility table; do not select Puppeteer and Chromium independently. - Deploy the known-good launch shape. Keep
chromium.args,defaultViewport,executablePath, andheadlessunchanged while diagnosing. - Raise memory. Start at 1600 MB, then compare startup duration and failure rate at lower values.
- Inspect
/tmp. Use an isolated profile, close every browser, and remove stale data when warm environments accumulate files. - Test networking independently. In a VPC, verify NAT, routes, security groups, NACLs, DNS, IAM, and ENI capacity.
- Evaluate migration. If the runtime is newer than the legacy table, test a maintained Chromium package or a container image with pinned versions.
9. Performance, reliability, and cost considerations
Memory and CPU
More memory gives Chromium more headroom and also gives the Lambda more CPU. Measure cold-start duration, navigation duration, and peak memory together; optimizing only the billed duration can increase failure retries.
Concurrency
Each concurrent invocation can start its own browser and consume memory, temporary storage, ENIs, NAT connections, and file descriptors. Ramp concurrency gradually. If failures begin only at higher concurrency, compare these shared limits before changing browser flags.
Temporary storage
Keep profiles and downloaded browser data bounded. A unique profile prevents two overlapping tasks from corrupting the same state; cleanup prevents warm environments from growing without limit.
Retries and idempotency
Retry launch failures with a short backoff only when the invocation is safe to repeat. Do not retry indefinitely: a bad version pair, missing binary, or missing NAT route will fail every time. Emit a request ID and phase in logs so a retry can be distinguished from a new job.
Or skip the browser setup
If your goal is a clean screenshot rather than maintaining Chromium in Lambda, ScreenshotNeo provides a GET endpoint that returns PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for the full parameter list.
cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const body = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', body);
ScreenshotNeo accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing result with X-Page-Verdict and X-Billed. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Does “socket hang up” mean the website blocked my request?
No. During launch(), it usually means the local Chromium process disconnected before Puppeteer completed its DevTools WebSocket handshake. A target-site block is more likely to appear during navigation.
Should I add --no-sandbox?
Start with chromium.args. Add flags only when logs identify a specific sandbox, shared-memory, GPU, or process problem.
Is 512 MB enough?
It is the documented minimum. The project recommends 1600 MB or more because memory also controls CPU and affects browser startup reliability.
Can a NAT gateway fix a launch-time localhost error?
Usually not. NAT fixes outbound VPC connectivity. Check it when navigation or page resources fail, while launch-time resets point first to Chromium startup, versions, memory, and temporary storage.
When should I replace chrome-aws-lambda?
Consider a maintained Chromium package or container image when your runtime or Puppeteer release is outside the legacy compatibility table, or when you need an actively maintained browser build.


