fix(browser): follow up batch failure and limit handling (#45506)

* fix(browser): propagate nested batch failures

* fix(browser): validate top-level batch limits

* test(browser): cover nested batch failures

* test(browser): cover top-level batch limits
This commit is contained in:
Vincent Koc 2026-03-13 18:39:28 -04:00 committed by GitHub
parent 7e49e98f79
commit e82ba71911
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 43 additions and 1 deletions

View File

@ -82,4 +82,23 @@ describe("batchViaPlaywright", () => {
targetId: "tab-1",
});
});
it("propagates nested batch failures to the parent batch result", async () => {
const result = await batchViaPlaywright({
cdpUrl: "http://127.0.0.1:9222",
targetId: "tab-1",
actions: [
{
kind: "batch",
actions: [{ kind: "evaluate", fn: "() => 1" }],
},
],
});
expect(result).toEqual({
results: [
{ ok: false, error: "act:evaluate is disabled by config (browser.evaluateEnabled=false)" },
],
});
});
});

View File

@ -845,7 +845,7 @@ async function executeSingleAction(
break;
case "batch":
// Nested batches: delegate recursively
await batchViaPlaywright({
const nestedResult = await batchViaPlaywright({
cdpUrl,
targetId: effectiveTargetId,
actions: action.actions,
@ -853,6 +853,10 @@ async function executeSingleAction(
evaluateEnabled,
depth: depth + 1,
});
const nestedFailure = nestedResult.results.find((result) => !result.ok);
if (nestedFailure) {
throw new Error(nestedFailure.error ?? "Nested batch action failed");
}
break;
default:
throw new Error(`Unsupported batch action kind: ${(action as { kind: string }).kind}`);

View File

@ -1043,6 +1043,9 @@ export function registerBrowserAgentActRoutes(
if (!actions.length) {
return jsonError(res, 400, "actions are required");
}
if (countBatchActions(actions) > MAX_BATCH_ACTIONS) {
return jsonError(res, 400, `batch exceeds maximum of ${MAX_BATCH_ACTIONS} actions`);
}
const targetIdError = validateBatchTargetIds(actions, tab.targetId);
if (targetIdError) {
return jsonError(res, 403, targetIdError);

View File

@ -287,6 +287,22 @@ describe("browser control server", () => {
slowTimeoutMs,
);
it(
"rejects oversized top-level batches before dispatch",
async () => {
const base = await startServerAndBase();
const batchRes = await postJson<{ error?: string }>(`${base}/act`, {
kind: "batch",
actions: Array.from({ length: 101 }, () => ({ kind: "press", key: "Enter" })),
});
expect(batchRes.error).toContain("batch exceeds maximum of 100 actions");
expect(pwMocks.batchViaPlaywright).not.toHaveBeenCalled();
},
slowTimeoutMs,
);
it("agent contract: hooks + response + downloads + screenshot", async () => {
const base = await startServerAndBase();