✦ Continue with Google
AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

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.

mediumConfidence 82%Next.jsAffected V16.2.4Affected V16.3.0-Canary.9

Origin Analysis

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.

Ecosystem Topology