vercel/next.js · error

block of .sst is the last block but not an index block…

Error message

block {} of {:08}.sst is the last block but not an index block (type {})

What it means

When parsing the final block of an SST file, turbo-persistence requires that the last block be the index block (type BLOCK_TYPE_INDEX). This error means the last block's type byte is something else, so the file's footer/index layout is corrupt or was written by an incompatible writer.

Solutions

  1. Delete the SST file or the whole persistent cache directory and rebuild the cache.
  2. Ensure the cache directory is not shared between different Turbopack/Next.js versions; clear it when switching versions.
  3. Check that writes to the cache volume complete cleanly (no abrupt shutdown, sufficient space).
  4. If it reproduces after a version change, report it with the file version metadata.
Defensive patterns

Strategy: fallback

Validate before calling

if (isSstFileTruncated(path)) { fs.rmSync(path, { force: true }); }

Try / catch

try {
  openCache(cacheDir);
} catch (e) {
  if (/not an index block/.test(String(e))) {
    fs.rmSync(cacheDir, { recursive: true, force: true });
    openCache(cacheDir);
  } else throw e;
}

Prevention

When it happens

Trigger: Reading a {:08}.sst file whose last block type byte does not equal BLOCK_TYPE_INDEX — truncated file tail, partially flushed write, or a file written by a different turbo-persistence version with a changed block format.

Common situations: Cache written by a newer/older Next.js or Turbopack version being read after an upgrade or downgrade; interrupted write leaving a partial block as the file tail.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of vercel/next.js@34433fd12e (2026-09-20). Data as JSON: /api/errors/7d2c530fd03588cc. Report an issue: GitHub.

Appendix: source

Thrown at turbopack/crates/turbo-persistence/src/static_sorted_file.rs:418

        verify_checksum(meta, data, checksum, block_index)?;

        ensure!(
            data.len() >= INDEX_BLOCK_HEADER_SIZE,
            "index block {} of {:08}.sst is too short ({} bytes)",
            block_index,
            meta.sequence_number,
            data.len()
        );
        ensure!(
            be::read_u8(data) == BLOCK_TYPE_INDEX,
            "block {} of {:08}.sst is the last block but not an index block (type {})",
            block_index,
            meta.sequence_number,
            be::read_u8(data)
        );
        let first_block = be::read_u16(&data[1..]);
        let entry_bytes = &data[INDEX_BLOCK_HEADER_SIZE..];
        ensure!(
            entry_bytes.len().is_multiple_of(INDEX_BLOCK_ENTRY_SIZE),
            "index block {} of {:08}.sst has {} trailing bytes past its last entry",
            block_index,
            meta.sequence_number,
            entry_bytes.len() % INDEX_BLOCK_ENTRY_SIZE
        );

        let entries = match backing {
            // Store a range, not the slice: `StaticSortedFile` owns the mmap these bytes live in.
            #[cfg(feature = "mmap")]
            StaticSortedFileBacking::Mmap(mmap) => {
                let start = entry_bytes.as_ptr() as usize - mmap.as_ptr() as usize;
                IndexEntries::Mmap(start..start + entry_bytes.len())
            }
            StaticSortedFileBacking::File { .. } => IndexEntries::Owned(entry_bytes.into()),
        };
        Ok(Self {
            entries,

View on GitHub (pinned to 34433fd12e)