fix(aws-lambda): surface sparticuz wedge as typed non-retryable error

Repeated Sandbox.Timedout chunks can leave @sparticuz/chromium
returning a falsy/empty path on subsequent invocations — warm
instances on the same execution environment never re-extract
chromium. The downstream puppeteer-core assertion about needing an
executablePath or channel buries the actionable cause; a cost-
analysis sweep took ~30 min to root-cause from that trace.

Guard the resolver: if mod.executablePath() returns a non-string,
empty string, or a path that does not exist on disk, throw a typed
ChromeBinaryUnavailableError whose message points at the recycle
remedy (env-var bump or redeploy). Add the error name to the three
NON_RETRYABLE lists so SFN short-circuits instead of burning four
15-min retries on a function that won't recover.

Same typed-error contract for the chrome-headless-shell fallback so
both sources fail consistently. Tests pin the wedge path (empty
string + non-existent file) and the carried metadata (source +
resolvedPath).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
James
2026-05-22 16:52:41 -04:00
committed by James Russo
co-authored by Claude Opus 4.7
parent ec1b7e1eff
commit 0f624f59fe
5 changed files with 123 additions and 14 deletions
@@ -65,6 +65,7 @@ const EXPECTED_NON_RETRYABLE_ERRORS = new Set([
"FONT_FETCH_FAILED",
"PLAN_TOO_LARGE",
"FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED",
"ChromeBinaryUnavailableError",
]);
function doSynth(): {
@@ -189,6 +189,9 @@ export class HyperframesRenderStack extends Construct {
* the snapshot test.
*/
private buildStateMachineDefinition(): sfn.IChainable {
// `ChromeBinaryUnavailableError` is non-retryable: a wedged warm
// instance keeps returning the same falsy executablePath until the
// env recycles, so retries just burn the 4× 15-min budget.
const NON_RETRYABLE_PLAN = [
"FFMPEG_VERSION_MISMATCH",
"PLAN_HASH_MISMATCH",
@@ -196,16 +199,19 @@ export class HyperframesRenderStack extends Construct {
"FONT_FETCH_FAILED",
"PLAN_TOO_LARGE",
"FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED",
"ChromeBinaryUnavailableError",
];
const NON_RETRYABLE_CHUNK = [
"FFMPEG_VERSION_MISMATCH",
"PLAN_HASH_MISMATCH",
"BROWSER_GPU_NOT_SOFTWARE",
"ChromeBinaryUnavailableError",
];
const NON_RETRYABLE_ASSEMBLE = [
"FFMPEG_VERSION_MISMATCH",
"PLAN_HASH_MISMATCH",
"FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED",
"ChromeBinaryUnavailableError",
];
const plan = new tasks.LambdaInvoke(this, "Plan", {