{"record":{"id":"33a2b9c146faa596","repo":"canopy-network/canopy","slug":"rollback-is-not-supported-for-nested-transactions","errorCode":null,"errorMessage":"rollback is not supported for nested transactions","messagePattern":"rollback is not supported for nested transactions","errorType":"error_code","errorClass":"ErrCommitDB","httpStatus":null,"severity":"error","filePath":"store/store.go","lineNumber":328,"sourceCode":"// the journal only needs keys\nfunc (s *Store) recordStateChangeKeys(version uint64) lib.ErrorI {\n\ts.ss.txn.l.Lock()\n\tkeys := make([][]byte, 0, len(s.ss.txn.ops))\n\tfor _, op := range s.ss.txn.ops {\n\t\tkeys = append(keys, bytes.Clone(op.key))\n\t}\n\ts.ss.txn.l.Unlock()\n\treturn s.Indexer.indexStateChangeKeys(version, keys)\n}\n\n// Rollback rewinds the store to a previous version (height).\n//\n// It removes all versioned entries above targetVersion, rebuilds the latest state\n// view from historical state at targetVersion, and resets the latest commit pointer.\n// NOTE: Rollback is an offline maintenance operation and must only run while the node is stopped.\nfunc (s *Store) Rollback(targetVersion uint64) lib.ErrorI {\n\tif s.isTxn {\n\t\treturn ErrCommitDB(fmt.Errorf(\"rollback is not supported for nested transactions\"))\n\t}\n\tif targetVersion == 0 {\n\t\treturn ErrCommitDB(fmt.Errorf(\"rollback target height must be >= 1\"))\n\t}\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tcurrentVersion := s.version\n\tif targetVersion > currentVersion {\n\t\treturn ErrCommitDB(fmt.Errorf(\"rollback target height %d exceeds current height %d\", targetVersion, currentVersion))\n\t}\n\tif targetVersion == currentVersion {\n\t\treturn nil\n\t}\n\n\tsnapshot := s.db.NewSnapshot()\n\tdefer snapshot.Close()\n","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/store/store.go#L310-L346","documentation":"Store.Rollback is an offline maintenance operation that rewinds the database to a target version. It cannot run while the store is a nested transaction, because a txn view lacks authoritative ownership of the underlying DB and rollback would corrupt the versioned state. The library rejects it eagerly with this error.","triggerScenarios":"Calling Rollback(targetVersion) on a Store whose isTxn flag is set (a transaction-scoped store), typically during offline maintenance scripts that accidentally obtained a txn handle instead of the root store.","commonSituations":"Running a rollback/rewind maintenance command against a store instance opened in transaction mode; code refactors that pass a txn store into the rollback utility.","solutions":["Open the root Store (not a transaction) for the rollback operation","Ensure the node is fully stopped before running rollback, as required for this offline operation","Refactor maintenance tooling to accept only the root store instance"],"exampleFix":"// before\ntxnStore, _ := store.NewTxn(parent, ...)\ntxnStore.Rollback(100) // error\n// after\nrootStore, _ := store.NewStore(db, ...)\nrootStore.Rollback(100)","handlingStrategy":"validation","validationCode":"func canRollback(s *store.Store) error {\n    if s.IsTxn() { return errors.New(\"rollback requires the root store, not a transaction\") }\n    return nil\n}","typeGuard":"func isRollbackTarget(s *store.Store) bool { return !s.IsTxn() }","tryCatchPattern":"if err := st.Rollback(h); err != nil {\n    if strings.Contains(err.Error(), \"nested transactions\") {\n        return rootStore.Rollback(h)\n    }\n    return err\n}","preventionTips":["Run rollback only from offline maintenance tooling that opens the root store directly","Assert node is stopped before any rollback operation","Never pass txn-scoped stores into maintenance utilities"],"tags":["store","transaction","rollback","maintenance"],"backgroundTag":"operation-not-supported","analyzedSha":"ee8197d91dd410f6592cb650a94c925ee6dc8bad","analyzedAt":"2026-09-06T09:30:15.973Z","contentChangedAt":"2026-09-06T09:30:15.973Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}