clockworklabs/SpacetimeDB · critical · std::io::Error

no valid commits in segment

Error message

no valid commits in segment

What it means

While resuming an existing commitlog (repo/mod.rs), a segment file longer than segment::Header::LEN is traversed to build its Metadata. If the traversed tx_range ends up empty, the file has bytes beyond the header but not a single usable commit, and resume fails with io::ErrorKind::InvalidData 'no valid commits in segment'.

Source

Thrown at crates/commitlog/src/repo/mod.rs:340

        .open_segment_reader(offset)
        .map_err(|source| with_segment_context("opening segment for resume", repo, offset, source))?;

    // If the segment at `offset` is empty, remove it and try the previous.
    // Return an error if no previous segment is found.
    let len = reader
        .segment_len()
        .map_err(|source| with_segment_context("determining segment file size for resume", repo, offset, source))?;
    if len <= segment::Header::LEN as u64 {
        debug!("repo {}: segment {} is empty", repo, offset);
        return Ok(ResumedSegment::Empty);
    }

    let guard_non_empty = |meta: &Metadata| match meta.tx_range.is_empty() {
        true => Err(with_segment_context(
            "checking metadata",
            repo,
            offset,
            io::Error::new(io::ErrorKind::InvalidData, "no valid commits in segment"),
        )),
        false => Ok(()),
    };

    // The segment is now guaranteed to be non-empty, i.e. contain more bytes
    // than the segment header.
    //
    // Traverse it to gather the `Metadata` and ensure that the segment is safe
    // to resume, which is the case if:
    //
    // - it contains at least one commit
    // - it does not contain corrupted commits
    // - the existing segment passes the compatibility check
    // - the existing segment's version is the same as
    //   the one requested in `opts`
    let offset_index = repo.get_offset_index(offset).ok();
    let meta = match Metadata::extract(offset, &mut reader, offset_index.as_ref()) {
        Err(error::SegmentMetadata::InvalidCommit { sofar, source }) => {

View on GitHub (pinned to 3653d2ed49)

Solutions

  1. Archive and remove the offending segment so resume continues from the previous valid segment, recovering lost transactions from a replica/upstream
  2. Prevent the torn tail in the first place: ensure graceful shutdown flushes commits, or trim trailing invalid data on open
  3. Verify disk and filesystem health if corruption is suspected
  4. Check that segment files copied into the repo were copied completely (checksum before restore)
Defensive patterns

Strategy: try-catch

Type guard

fn is_empty_segment_resume(err: &io::Error) -> bool {
    err.kind() == io::ErrorKind::InvalidData && err.to_string().contains("no valid commits in segment")
}

Try / catch

match commitlog.resume(&repo, &opts) {
    Ok(state) => Ok(state),
    Err(e) if is_empty_segment_resume(&e) => {
        // quarantine the named segment and retry resume without it,
        // recovering the gap from a replica/upstream
        quarantine_last_segment(&repo)?;
        commitlog.resume(&repo, &opts)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Resuming after a crash that left a partially written or torn first commit in a fresh segment; a segment file preallocated with zeroes and longer than the header; on-disk corruption in the first commit record; copying a truncated segment file into the repo directory.

Common situations: Unclean shutdown (kill -9, power loss) immediately after segment creation; restoring partial backups; disk corruption or bit rot on the segment's first records.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@3653d2ed49 (2026-08-20). Data as JSON: /api/errors/13a2fdd74fcc2c4d. Report an issue: GitHub.