{"record":{"id":"15b1a14fbd2bc9cb","repo":"canopy-network/canopy","slug":"nested-transactions-are-not-supported","errorCode":null,"errorMessage":"nested transactions are not supported","messagePattern":"nested transactions are not supported","errorType":"error_code","errorClass":"ErrCommitDB","httpStatus":null,"severity":"error","filePath":"store/store.go","lineNumber":253,"sourceCode":"\treturn &Store{\n\t\tversion:    s.version,\n\t\tlog:        s.log,\n\t\tdb:         s.db,\n\t\twriter:     writer,\n\t\tss:         s.ss.Copy(lssReader, lssReader),\n\t\tIndexer:    &Indexer{s.Indexer.db.Copy(reader, reader), s.config},\n\t\tmetrics:    s.metrics,\n\t\tmu:         &sync.Mutex{},\n\t\tcompaction: atomic.Bool{},\n\t\tbackup:     atomic.Bool{},\n\t}, nil\n}\n\n// Commit() performs a single atomic write of the current state to all stores.\nfunc (s *Store) Commit() (root []byte, err lib.ErrorI) {\n\t// nested transactions should only flush changes to the parent transaction, not the database\n\tif s.isTxn {\n\t\treturn nil, ErrCommitDB(fmt.Errorf(\"nested transactions are not supported\"))\n\t}\n\ts.mu.Lock()         // lock commit op\n\tdefer s.mu.Unlock() // unlock commit op\n\tstartTime := time.Now()\n\t// get the root from the sparse merkle tree at the current state\n\troot, err = s.Root()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnextVersion := s.version + 1\n\t// set the new CommitID (to the Transaction not the actual DB)\n\tif err = s.setCommitID(nextVersion, root); err != nil {\n\t\ts.Reset()\n\t\treturn nil, err\n\t}\n\t// collect LSS tombstones before Flush() clears the txn operations\n\tlssDeleteKeys := s.collectLssDeleteKeys()\n\t// Persist the keys touched by this commit outside consensus state.","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/store/store.go#L235-L271","documentation":"Store.Commit performs a single atomic write of the current state to all backing stores. When the Store is a nested transaction (created via NewTxn with isTxn), Commit is not allowed to flush to the database — nested transactions must only flush changes to their parent — so this error is returned instead.","triggerScenarios":"Calling Commit() on a Store instance obtained through a transaction/nested store (isTxn=true), e.g. calling the top-level Commit on a txn-scoped store handle inside tests or application code that holds a transaction view.","commonSituations":"Application code mistakenly holds a reference to a transactional store and calls Commit directly instead of committing via the parent/root store; refactoring that moved a Commit call inside a transaction scope.","solutions":["Call Commit() on the root/parent Store, not on the transaction-scoped store","If working within a transaction, use the transaction's commit/flush-to-parent mechanism instead of the DB-level Commit","Restructure code so only one store instance with ownsDB/isTxn=false performs commits"],"exampleFix":"// before\ntxn, _ := store.NewTxn(...)\nroot, err := txn.Commit() // nested -> error\n// after\nroot, err := parentStore.Commit() // commit at the root store\n// txn changes are flushed to parent via the txn commit path","handlingStrategy":"type-guard","validationCode":"if s.IsTxn() { // expose or check txn flag before committing\n    return errors.New(\"cannot commit a nested transaction; commit via parent store\")\n}","typeGuard":"func isRootStore(s *store.Store) bool { return !s.IsTxn() }","tryCatchPattern":"root, err := st.Commit()\nif err != nil && strings.Contains(err.Error(), \"nested transactions\") {\n    // wrong handle: retry with the parent store reference\n    return parent.Commit()\n}","preventionTips":["Keep a single canonical reference to the root store for commits","Type-wrap transaction handles so the compiler distinguishes txn vs root stores","Review code paths introduced during refactors that pass store handles into commit sites"],"tags":["store","transaction","commit"],"backgroundTag":"invalid-state-transition","analyzedSha":"ee8197d91dd410f6592cb650a94c925ee6dc8bad","analyzedAt":"2026-09-06T09:30:15.973Z","contentChangedAt":"2026-09-06T09:30:15.973Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}