fix(cli): a browser a sandbox refuses says to rerun the command with escalation (#5734)

This commit is contained in:
Miguel Ángel
2026-10-17 07:36:52 +00:00
committed by GitHub
parent caea3863b0
commit fa93669156
4 changed files with 50 additions and 0 deletions
+4
View File
@@ -7,6 +7,7 @@ import {
import { normalizeErrorMessage } from "../utils/errorMessage.js";
import { requestCliExit } from "../utils/commandResult.js";
import { processIsAlive } from "../utils/processTree.js";
import { sandboxDeniedRemediation } from "./sandboxDenied.js";
export class BrowserCleanupError extends AggregateError {}
@@ -94,6 +95,9 @@ export async function launchManagedBrowser(
browsers.add(browser);
child.once("exit", () => browsers.delete(browser));
return browser;
} catch (error) {
const remediation = sandboxDeniedRemediation(normalizeErrorMessage(error));
throw remediation ? new Error(remediation, { cause: error }) : error;
} finally {
pending.delete(launch);
}
@@ -0,0 +1,30 @@
import type { LaunchOptions, PuppeteerNode } from "puppeteer-core";
import { describe, expect, it } from "vitest";
import { launchManagedBrowser } from "./launch.js";
const SEATBELT_LAUNCH_FAILURE = [
"Failed to launch the browser process: Code: null",
"",
"stderr:",
"[1016/170315.389548:FATAL:base/apple/mach_port_rendezvous_mac.cc:159] Check failed: kr == KERN_SUCCESS. bootstrap_check_in org.chromium.Chromium.MachPortRendezvousServer.26166: Permission denied (1100)",
].join("\n");
const failingPuppeteer = (message: string) =>
({ launch: async (_: LaunchOptions) => Promise.reject(new Error(message)) }) as PuppeteerNode;
describe("a browser launch a sandbox refuses", () => {
it("tells the caller to rerun with escalation instead of showing Chrome's crash", async () => {
await expect(
launchManagedBrowser(failingPuppeteer(SEATBELT_LAUNCH_FAILURE), {}),
).rejects.toMatchObject({
message:
"The browser can't start inside this sandbox: macOS refused Chrome's mach service (bootstrap_check_in: Permission denied).\nRerun this command with escalation, outside the sandbox.",
cause: { message: SEATBELT_LAUNCH_FAILURE },
});
});
it("leaves any other launch failure as it was", async () => {
const other = "Failed to launch the browser process: Code: 1";
await expect(launchManagedBrowser(failingPuppeteer(other), {})).rejects.toThrow(other);
});
});
+10
View File
@@ -0,0 +1,10 @@
const MACH_SERVICE_DENIED = /bootstrap_check_in\b[^\n]*Permission denied/;
/** A process sandbox (an agent's macOS seatbelt) refused Chrome its mach service, so no browser starts here. */
export function sandboxDeniedRemediation(errorMessage: string): string | undefined {
if (!MACH_SERVICE_DENIED.test(errorMessage)) return undefined;
return [
"The browser can't start inside this sandbox: macOS refused Chrome's mach service (bootstrap_check_in: Permission denied).",
"Rerun this command with escalation, outside the sandbox.",
].join("\n");
}
+6
View File
@@ -118,6 +118,7 @@ import {
import { chromeLaunchRemediation } from "../browser/linuxDeps.js";
import { macosOldChromeCrashRemediation } from "../browser/macosOldChromeCrash.js";
import { windowsChromeCrashRemediation } from "../browser/windowsCrash.js";
import { sandboxDeniedRemediation } from "../browser/sandboxDenied.js";
import { killOrphanedProcessesForRender } from "../utils/orphanCleanup.js";
import { createRenderCancellationScope } from "../utils/renderCancellation.js";
import {
@@ -1759,6 +1760,11 @@ function handleRenderError(
// failure on Linux/WSL is an environment problem, not a composition bug.
// Replace the generic "Try --docker" hint with the exact per-distro
// remediation and a pointer at `doctor`.
const sandboxRemediation = sandboxDeniedRemediation(message);
if (sandboxRemediation) {
errorBox("Render failed — Chrome could not launch", message, sandboxRemediation);
failCommand();
}
const remediation = chromeLaunchRemediation(message);
if (remediation) {
errorBox("Render failed — Chrome could not launch", message, remediation);