kopia/kopia · error

error loading write epoch

Error message

error loading write epoch

What it means

loadWriteEpoch wraps a failure from blob.ListAllBlobs when enumerating epoch marker index blobs (EpochMarkerIndexBlobPrefix) to rebuild the CurrentSnapshot's write epoch state. Without listing these blobs the manager cannot determine the current write epoch or per-epoch start times, so snapshot loading fails.

Solutions

  1. Fix the wrapped storage error: verify blob store connectivity, credentials, and that the epoch marker index container/prefix exists.
  2. Confirm the storage prefix EpochMarkerIndexBlobPrefix matches where epoch markers were actually written (no version/migration drift).
  3. Retry snapshot loading — transient list failures clear once the backend recovers.
  4. Check storage backend quotas/throttling if the error is rate limiting.

Example fix

// before: fail startup hard on transient list error
if err := mgr.loadWriteEpoch(ctx, snapshot); err != nil { return err }

// after: brief retry for transient storage errors
err := mgr.loadWriteEpoch(ctx, snapshot)
if err != nil && isTransientStorage(err) {
    time.Sleep(2 * time.Second)
    err = mgr.loadWriteEpoch(ctx, snapshot)
}
Defensive patterns

Strategy: retry

Validate before calling

// probe storage before snapshot load
if _, err := blob.ListAllBlobs(ctx, st, EpochMarkerIndexBlobPrefix); err != nil {
    return fmt.Errorf("epoch marker index not listable: %w", err)
}

Try / catch

if err := mgr.loadWriteEpoch(ctx, snapshot); err != nil {
    if strings.Contains(err.Error(), "error loading write epoch") {
        // storage list failure: check blob store health/credentials, retry after backoff
    }
    return err
}

Prevention

When it happens

Trigger: Calling verifyCurrentWriteEpoch (or snapshot load via the anonymous caller) when the underlying blob storage returns an error listing blobs under the EpochMarkerIndex prefix — storage outage, permission failure, malformed store response.

Common situations: Blob store connectivity loss or expired credentials during node startup; bucket/container missing or renamed after migration; storage backend returning throttling/5xx errors that ListAllBlobs does not absorb.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/2516e363a1676c99. Report an issue: GitHub.

Appendix: source

Thrown at internal/epoch/epoch_manager.go:499

	for err := e.refreshAttemptLocked(ctx); err != nil; err = e.refreshAttemptLocked(ctx) {
		if ctx.Err() != nil {
			return errors.Wrap(ctx.Err(), "refreshAttemptLocked")
		}

		contentlog.Log2(ctx, e.log, "refresh attempt failed", logparam.Error("error", err), logparam.Duration("nextDelayTime", nextDelayTime))
		time.Sleep(nextDelayTime)

		nextDelayTime = min(time.Duration(float64(nextDelayTime)*maxRefreshAttemptSleepExponent), maxRefreshAttemptSleep)
	}

	return nil
}

func (e *Manager) loadWriteEpoch(ctx context.Context, cs *CurrentSnapshot) error {
	blobs, err := blob.ListAllBlobs(ctx, e.st, EpochMarkerIndexBlobPrefix)
	if err != nil {
		return errors.Wrap(err, "error loading write epoch")
	}

	for epoch, bm := range groupByEpochNumber(blobs) {
		cs.EpochStartTime[epoch] = bm[0].Timestamp

		if epoch > cs.WriteEpoch {
			cs.WriteEpoch = epoch
		}
	}

	cs.EpochMarkerBlobs = blobs

	return nil
}

func (e *Manager) loadDeletionWatermark(ctx context.Context, cs *CurrentSnapshot) error {
	blobs, err := blob.ListAllBlobs(ctx, e.st, DeletionWatermarkBlobPrefix)
	if err != nil {

View on GitHub (pinned to 82495e54b5)