Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/fix-sourcemap-crash-invalid-column.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

Prevent `wrangler dev` crash when source-mapping a truncated error chunk

When a worker logs many errors in quick succession, the stderr chunks received by `wrangler dev` can be truncated mid-stack-frame, leaving a call site with an invalid column number. The source map library throws in that case, which was crashing the wrangler process entirely. The error is now caught and the original (un-source-mapped) text is returned instead.
30 changes: 28 additions & 2 deletions packages/deploy-helpers/src/deploy/helpers/sourcemap.ts
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import assert from "node:assert";
import url from "node:url";
import { maybeGetFile } from "@cloudflare/workers-shared";
import { getFreshSourceMapSupport } from "miniflare";
import { logger } from "../../shared/context";
import type { Options } from "@cspotcode/source-map-support";
import type Protocol from "devtools-protocol";

Expand Down Expand Up @@ -122,6 +123,27 @@ function callFrameToCallSite(frame: Protocol.Runtime.CallFrame): CallSite {
});
}

/**
* Calls `prepareStack` and returns `null` if it throws (e.g. when a truncated
* stderr chunk produces an invalid column number). Returning `null` lets
* `getSourceMappedString` fall back to the original string without executing
* the replacement loop against a partially-computed result.
*/
function tryPrepareStack(
prepareStack: ReturnType<typeof getSourceMappingPrepareStackTrace>,
error: Error,
callSites: NodeJS.CallSite[]
): string | null {
try {
return prepareStack(error, callSites);
} catch (err) {
logger?.debug(
`Source map application failed, falling back to original stack trace: ${err}`
);
return null;
}
}

const placeholderError = new Error();
export function getSourceMappedString(
value: string,
Expand All @@ -138,16 +160,20 @@ export function getSourceMappedString(
const callSiteLines = Array.from(value.matchAll(CALL_SITE_REGEXP));
const callSites = callSiteLines.map(lineMatchToCallSite);
const prepareStack = getSourceMappingPrepareStackTrace(retrieveSourceMap);
const sourceMappedStackTrace: string = prepareStack(
const sourceMappedStackTrace = tryPrepareStack(
prepareStack,
placeholderError,
callSites
);
if (sourceMappedStackTrace === null) {
return value;
}
const sourceMappedCallSiteLines = sourceMappedStackTrace.split("\n").slice(1);

for (let i = 0; i < callSiteLines.length; i++) {
// If a call site doesn't have a file name, it's likely invalid, so don't
// apply source mapping (see cloudflare/workers-sdk#4668)
if (callSites[i].getFileName() === undefined) {
if (callSites[i].getFileName() === null) {
Comment thread
petebacondarwin marked this conversation as resolved.
continue;
}

Expand Down
14 changes: 14 additions & 0 deletions packages/deploy-helpers/tests/sourcemap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, it } from "vitest";
import { getSourceMappedString } from "../src/deploy/helpers/sourcemap";

describe("getSourceMappedString", () => {
it("returns original value when source mapping throws", ({ expect }) => {
const value = `Error: test\n at Object.<anonymous> (/some/file.js:1:1)`;

const result = getSourceMappedString(value, () => {
throw new Error("simulated source map failure");
});

expect(result).toBe(value);
});
});
Loading