canopy-network/canopy · error · ErrCommitDB

rollback target height must be >= 1

Error message

rollback target height must be >= 1

What it means

Rollback requires a positive target height: version 0 does not exist as a rollback destination (state versions start at 1). Passing targetVersion == 0 is rejected with this error before any database work is done.

Source

Thrown at store/store.go:331

	keys := make([][]byte, 0, len(s.ss.txn.ops))
	for _, op := range s.ss.txn.ops {
		keys = append(keys, bytes.Clone(op.key))
	}
	s.ss.txn.l.Unlock()
	return s.Indexer.indexStateChangeKeys(version, keys)
}

// Rollback rewinds the store to a previous version (height).
//
// It removes all versioned entries above targetVersion, rebuilds the latest state
// view from historical state at targetVersion, and resets the latest commit pointer.
// NOTE: Rollback is an offline maintenance operation and must only run while the node is stopped.
func (s *Store) Rollback(targetVersion uint64) lib.ErrorI {
	if s.isTxn {
		return ErrCommitDB(fmt.Errorf("rollback is not supported for nested transactions"))
	}
	if targetVersion == 0 {
		return ErrCommitDB(fmt.Errorf("rollback target height must be >= 1"))
	}
	s.mu.Lock()
	defer s.mu.Unlock()

	currentVersion := s.version
	if targetVersion > currentVersion {
		return ErrCommitDB(fmt.Errorf("rollback target height %d exceeds current height %d", targetVersion, currentVersion))
	}
	if targetVersion == currentVersion {
		return nil
	}

	snapshot := s.db.NewSnapshot()
	defer snapshot.Close()

	// Ensure the target commit exists so we can repoint the latest commit id.
	targetReader := NewVersionedStore(snapshot, nil, targetVersion)
	targetTx := NewTxn(targetReader, nil, nil, false, false, true)

View on GitHub (pinned to ee8197d91d)

Solutions

  1. Pass a target height >= 1, typically the last known good block height
  2. Check the CLI/config parsing that supplies targetVersion and guard against zero values before calling Rollback
  3. If the intent is 'rewind one block', compute currentVersion - 1 instead of using 0

Example fix

// before
if err := store.Rollback(targetHeight); err != nil {...} // targetHeight==0
// after
if targetHeight < 1 {
    return fmt.Errorf("--height must be provided (>=1)")
}
if err := store.Rollback(targetHeight); err != nil {...}
Defensive patterns

Strategy: validation

Validate before calling

if targetVersion < 1 {
    return fmt.Errorf("rollback target height must be >= 1, got %d", targetVersion)
}

Try / catch

if err := st.Rollback(h); err != nil {
    if strings.Contains(err.Error(), "must be >= 1") {
        return fmt.Errorf("invalid --height flag: must supply a block height >= 1")
    }
    return err
}

Prevention

When it happens

Trigger: Calling Store.Rollback(0), e.g. a misconfigured CLI flag or a variable that defaulted to zero because the target height was never parsed/loaded from config.

Common situations: Rollback command invoked with a missing --height flag defaulting to 0; a script computing target height from an unset environment variable; off-by-one logic mapping 'genesis' to version 0.

Related errors


AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06). Data as JSON: /api/errors/4da71075fe389aef. Report an issue: GitHub.