weaviate/weaviate · error

cutoffMs must be > 0, got %d

Error message

cutoffMs must be > 0, got %d

What it means

createAsyncCheckpoints validates its cutoffMs argument before doing any work and rejects non-positive values with "cutoffMs must be > 0, got %d". The cutoff (epoch milliseconds) defines which async-replication changes the checkpoint covers, so a zero/negative value is meaningless input.

Source

Thrown at adapters/repos/db/index_async_checkpoint.go:201

// asyncCheckpointBroadcaster is the fan-out seam; tests substitute a stub.
type asyncCheckpointBroadcaster interface {
	LocalNodeName() string
	BroadcastCreateAsyncCheckpoint(ctx context.Context, shardNames []string, cutoffMs int64, createdAt time.Time) (successes, failures int)
	BroadcastDeleteAsyncCheckpoint(ctx context.Context, shardNames []string) (successes, failures int)
	BroadcastGetAsyncCheckpointStatus(ctx context.Context, shardNames []string) (statuses map[string][]replica.AsyncCheckpointNodeStatus, successes, failures int)
}

// CreateAsyncCheckpoints picks one createdAt for the whole fan-out so every
// replica records the same convergence tie-breaker. Best-effort: per-shard
// failures are logged, but the call returns nil and divergence reconciles
// on the next cycle.
func (i *Index) CreateAsyncCheckpoints(ctx context.Context, cutoffMs int64, shards []string) error {
	return i.createAsyncCheckpoints(ctx, cutoffMs, shards, i.replicator)
}

func (i *Index) createAsyncCheckpoints(ctx context.Context, cutoffMs int64, shards []string, broadcaster asyncCheckpointBroadcaster) error {
	if cutoffMs <= 0 {
		return fmt.Errorf("cutoffMs must be > 0, got %d", cutoffMs)
	}
	targets := i.resolveShardNames(shards)
	createdAt := time.Now().UTC()

	var localSuccesses, localFailures atomic.Int64
	eg, egCtx := enterrors.NewErrorGroupWithContextWrapper(i.logger, ctx)
	eg.SetLimit(_NUMCPU)
	for _, shardName := range targets {
		shardName := shardName
		eg.Go(func() error {
			if err := i.createAsyncCheckpoint(egCtx, shardName, cutoffMs, createdAt); err != nil {
				localFailures.Add(1)
				// Debug, not Warn: "shard not loaded here" is expected for fan-out.
				i.logger.WithFields(logrus.Fields{
					"action": "async_checkpoint_local",
					"op":     "create",
					"class":  i.Config.ClassName,
					"shard":  shardName,

View on GitHub (pinned to 75aa4b6d11)

Solutions

  1. Pass a positive cutoff in epoch milliseconds, e.g. time.Now().UTC().UnixMilli().
  2. Validate the cutoff at the call site before invoking CreateAsyncCheckpoints.
  3. If the cutoff was computed, log/inspect its derivation to find where it became zero.

Example fix

// before
cutoff := int64(0)
idx.CreateAsyncCheckpoints(ctx, cutoff, shards) // rejected
// after
cutoff := time.Now().UTC().UnixMilli()
idx.CreateAsyncCheckpoints(ctx, cutoff, shards)
Defensive patterns

Strategy: validation

Validate before calling

if cutoffMs <= 0 {
	return fmt.Errorf("cutoffMs must be positive, got %d", cutoffMs)
}

Try / catch

if err := idx.CreateAsyncCheckpoints(ctx, cutoffMs, shards); err != nil {
	if strings.Contains(err.Error(), "cutoffMs must be > 0") {
		return fmt.Errorf("caller bug: cutoff derived from %v", cutoffSource)
	}
	return err
}

Prevention

When it happens

Trigger: Calling Index.CreateAsyncCheckpoints(ctx, cutoffMs, shards) with cutoffMs == 0 or negative — e.g. an uninitialized int64, a failed time computation, or a caller passing 0 as a default.

Common situations: Application code computing a cutoff timestamp from an unset variable; marshaling bugs that zero out the timestamp; misuse of the internal API in tests/tools.

Understand the failure class

Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.

Related errors


AI-assisted analysis of weaviate/weaviate@75aa4b6d11 (2026-09-04). Data as JSON: /api/errors/6c7966b9b16074f8. Report an issue: GitHub.