juicedata/juicefs · error

Cannot overwrite uploaded block: %d < %d

Error message

Cannot overwrite uploaded block: %d < %d

What it means

After part of a wSlice has been uploaded (s.uploaded marks the boundary), earlier bytes are considered immutable. WriteAt rejects any write whose offset is below the uploaded boundary to preserve consistency between staged data and already-persisted blocks. This prevents silently rewriting data that object storage already received.

Source

Thrown at pkg/chunk/cached_store.go:260

		writeback: store.conf.Writeback,
		tierID:    tierID,
	}
}

func (s *wSlice) SetID(id uint64) {
	s.id = id
}

func (s *wSlice) SetWriteback(enabled bool) {
	s.writeback = enabled
}

func (s *wSlice) WriteAt(p []byte, off int64) (n int, err error) {
	if int(off)+len(p) > chunkSize {
		return 0, fmt.Errorf("write out of chunk boundary: %d > %d", int(off)+len(p), chunkSize)
	}
	if off < int64(s.uploaded) {
		return 0, fmt.Errorf("Cannot overwrite uploaded block: %d < %d", off, s.uploaded)
	}

	// Fill previous blocks with zeros
	if s.length < int(off) {
		zeros := make([]byte, int(off)-s.length)
		_, _ = s.WriteAt(zeros, int64(s.length))
	}

	for n < len(p) {
		indx := s.index(int(off) + n)
		boff := (int(off) + n) % s.store.conf.BlockSize
		var bs = pageSize
		if indx > 0 || bs > s.store.conf.BlockSize {
			bs = s.store.conf.BlockSize
		}
		bi := boff / bs
		bo := boff % bs
		var page *Page

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Only append beyond s.uploaded; compute write offsets relative to the current uploaded boundary.
  2. Do not reuse a wSlice to rewrite uploaded regions — create a new slice/chunk for corrected data.
  3. Check caller's offset bookkeeping (writeback/append position) to ensure it never goes backwards.
  4. If triggered by retries, make retry logic idempotent by skipping writes below the uploaded boundary instead of reissuing them.

Example fix

// before
slice.WriteAt(data, oldOff) // oldOff < slice.uploaded
// after
if int64(oldOff) < int64(slice.uploaded) {
    return fmt.Errorf("region already uploaded, skipping rewrite at %d", oldOff)
}
slice.WriteAt(data, oldOff)
Defensive patterns

Strategy: validation

Validate before calling

if int64(off) < int64(slice.uploaded) {
    return errors.New("cannot write below uploaded boundary; use an append offset")
}

Try / catch

n, err := slice.WriteAt(p, off)
if err != nil && strings.Contains(err.Error(), "Cannot overwrite uploaded block") {
    // advance offset or allocate a new slice; never retry same offsets
}

Prevention

When it happens

Trigger: Calling wSlice.WriteAt with an off smaller than s.uploaded — e.g. re-writing an already-flushed prefix of the slice, a retry that re-issues an old write, or an offset-tracking bug where the caller's file position lags the uploaded boundary.

Common situations: Random-write workloads revisiting earlier regions of an append-only staging file; crash recovery or writeback replays reapplying stale writes; misordered internal upload/write pipelines in custom data paths.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/e7777236c0eb7ef5. Report an issue: GitHub.