clockworklabs/SpacetimeDB · error · io::Error

failed to read checksum: {e}

Error message

failed to read checksum: {e}

What it means

A commit ends with a 4-byte CRC32C checksum after its records; failing to read those bytes (the error kind is preserved in the message) means the file ends or errors between payload and checksum. It is the same torn-write family as the payload-read error, landing exactly at the checksum boundary of the final commit.

Source

Thrown at crates/commitlog/src/commit.rs:322

        let v = if log_format_version == 0 {
            Version::V0
        } else {
            Version::V1
        };
        let Some(hdr) = Header::decode_internal(&mut reader, v)? else {
            return Ok(None);
        };
        let mut records = vec![0; hdr.len as usize];
        reader.read_exact(&mut records).map_err(|e| {
            io::Error::new(
                e.kind(),
                format!("failed to read {} bytes of commit payload: {}", hdr.len, e),
            )
        })?;

        let chk = reader.crc32c();
        let crc = decode_u32(reader.into_inner())
            .map_err(|e| io::Error::new(e.kind(), format!("failed to read checksum: {e}")))?;

        if chk != crc {
            return Err(invalid_data(ChecksumMismatch));
        }

        Ok(Some(Self {
            min_tx_offset: hdr.min_tx_offset,
            epoch: hdr.epoch,
            n: hdr.n,
            records,
            checksum: crc,
        }))
    }

    /// Convert `self` into an iterator yielding [`Transaction`]s.
    ///
    /// The supplied [`Decoder`] is responsible for extracting individual
    /// transactions from the `records` buffer.

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Same as a truncated payload: let recovery truncate the incomplete final commit and open at the prefix
  2. Restore from backup if the repository will not open
  3. Prevent torn writes: graceful shutdown, disk space monitoring, consistent snapshots
Defensive patterns

Strategy: try-catch

Try / catch

match commit.decode(&mut reader) {
    Ok(None) => break,
    Ok(Some(c)) => commits.push(c),
    Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
        // checksum never arrived: final commit is incomplete; the prefix up to the
        // previous commit is still valid
        tracing::warn!("commitlog truncated before checksum: {e}");
        break;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Records read successfully but the trailing 4-byte CRC cannot be read: the last commit was written up to or into its checksum and then cut off.

Common situations: Same scenarios as a truncated payload — hard crash mid-commit, disk full, copying db files while the host writes — landing at the checksum boundary.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/d943a6722691a863. Report an issue: GitHub.