Turbopack Emits Invalid VLQ Column Deltas In Source Maps For Already-Minified Vendor Sources (Firefox Rejects With `Has Invalid "Mappings"`)
Turbopack's dev-only indexed source maps contain VLQ column deltas with values near 2^32, caused by a signed/unsigned wraparound when encoding negative srcCol deltas on long minified lines. Firefox rejects the map; Chrome tolerates silently. Affects any pre-minified vendor file, not just simple-peer.
The Turbopack source map builder for indexed/sectioned maps (used in `next dev --turbopack`) likely casts column deltas to a 32-bit unsigned integer before VLQ encoding. Small negative `srcCol` deltas become huge positive values (~2^32), producing structurally impossible column positions. The same input produces clean flat maps in `next build`, confirming the bug is in the dev-only sectioned encoder path.
1. pnpm install (uses next@16.3.0-canary.9 and simple-peer@9.11.1)
2. pnpm dev (next dev --turbopack -p 3099)
3. Open http://localhost:3099/ in Firefox
4. Open DevTools console; observe 'Source Map ... has invalid "mappings"'.
The affected map contains VLQ tokens like `IAKw/67//H` decoding to srcCol deltas over 4 billion.
Fixing Code Block
// In turbopack/crates/turbopack-core/src/source_map/source_map_builder.rs
const BASE64_CHARS: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
fn vlq_encode(mut value: i64) -> String {
let mut out = String::new();
loop {
let mut digit = (value & 0b11111) as u8;
value >>= 5;
if value != 0 {
digit |= 0b100000;
}
out.push(BASE64_CHARS[digit as usize] as char);
if value == 0 {
break;
}
}
out
}
fn encode_signed(value: i64) -> String {
let vlq = if value < 0 {
((-value) << 1) + 1
} else {
value << 1
};
vlq_encode(vlq)
}
// In the mapping loop, compute deltas as i64 to avoid u32 wraparound:
let gen_col_delta = gen_col as i64 - prev_gen_col as i64;
let src_idx_delta = src_idx as i64 - prev_src_idx as i64;
let src_line_delta = src_line as i64 - prev_src_line as i64;
let src_col_delta = src_col as i64 - prev_src_col as i64;
let segment = format!(
"{}{}{}{}",
encode_signed(gen_col_delta),
encode_signed(src_idx_delta),
encode_signed(src_line_delta),
encode_signed(src_col_delta)
);
Replace any u32/i32 arithmetic in the VLQ segment encoder with i64 arithmetic, and ensure the encoder accepts signed 64-bit integers. This eliminates the u32 wraparound that turns small negative column deltas into ~2^32 values. The signed VLQ encoding remains spec-compliant and prevents Firefox decoder rejections.
Edge Case Audit
The patch changes the encoder data type to i64. On 32-bit architectures, extreme source column values (>2^31) could still overflow during the `<< 1` operation, but real files never reach such sizes. Ensure the fix is validated against the existing source map test suite and manually verified with Firefox for pre-minified vendor files. Rollback: revert the commit; no persisted data migration is required.