{"record":{"id":"cf9680fc9e8cede7","repo":"ethereum/go-ethereum","slug":"duplicated-flush-operation","errorCode":null,"errorMessage":"duplicated flush operation","messagePattern":"duplicated flush operation","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"triedb/pathdb/buffer.go","lineNumber":137,"sourceCode":"\treturn b.layers == 0\n}\n\n// full returns an indicator if the size of accumulated content exceeds the\n// configured threshold.\nfunc (b *buffer) full() bool {\n\treturn b.size() > b.limit\n}\n\n// size returns the approximate memory size of the held content.\nfunc (b *buffer) size() uint64 {\n\treturn b.states.size + b.nodes.size\n}\n\n// flush persists the in-memory dirty trie node into the disk if the configured\n// memory threshold is reached. Note, all data must be written atomically.\nfunc (b *buffer) flush(root common.Hash, db ethdb.KeyValueStore, freezers []ethdb.AncientWriter, progress []byte, nodesCache, statesCache *fastcache.Cache, id uint64, postFlush func()) {\n\tif b.done != nil {\n\t\tpanic(\"duplicated flush operation\")\n\t}\n\tb.done = make(chan struct{}) // allocate the channel for notification\n\n\t// Schedule the background thread to construct the batch, which usually\n\t// take a few seconds.\n\tgo func() {\n\t\tdefer func() {\n\t\t\tif postFlush != nil {\n\t\t\t\tpostFlush()\n\t\t\t}\n\t\t\tclose(b.done)\n\t\t}()\n\n\t\t// Ensure the target state id is aligned with the internal counter.\n\t\thead := rawdb.ReadPersistentStateID(db)\n\t\tif head+b.layers != id {\n\t\t\tb.flushErr = fmt.Errorf(\"buffer layers (%d) cannot be applied on top of persisted state id (%d) to reach requested state id (%d)\", b.layers, head, id)\n\t\t\treturn","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/ethereum/go-ethereum/blob/6bb0588ad8e7f922e4ad5580f51265a4097af08f/triedb/pathdb/buffer.go#L119-L155","documentation":"pathdb's in-memory buffer flushes dirty nodes to disk asynchronously, and flush marks the buffer as consumed by setting b.done. A second flush on the same buffer means the database attempted to persist two overlapping buffer generations — a lifecycle invariant violation that would risk writing inconsistent state, so it panics immediately.","triggerScenarios":"buffer.flush being invoked twice on the same buffer object — e.g. two concurrent commits hitting the memory limit, or a commit racing with a database close/reopen that re-flushes an already-draining buffer.","commonSituations":"Concurrent calls to triedb Commit/Flush from multiple goroutines without the outer lock; fork modifications that call flush manually; bugs in the layering that reuse a buffer after checkpointing.","solutions":["Serialize commits: pathdb commits must go through the single db.commit lock; check for a fork that bypassed it","Inspect the goroutine stacks in the panic dump to find which two paths both reached flush, and gate the second on the first's completion via the done channel","Upgrade/patch if this reproduces on stock geth — it is an internal invariant break, not caller error"],"exampleFix":"// before\n// two goroutines both trigger the limit path\ngo db.Commit(...)  // flush #1\ngo db.Commit(...)  // flush #2 -> duplicated flush operation\n\n// after\ndb.lock.Lock()\ndefer db.lock.Unlock()\ndb.Commit(...) // flush serialized under the db lock","handlingStrategy":"validation","validationCode":"// (Internal) callers must ensure single flush per buffer generation\nif b.done != nil {\n    return errors.New(\"buffer already flushing\") // hypothetical guard if you fork flush\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        log.Crit(\"pathdb double flush\", \"panic\", r) // crash-dump, do not continue serving state\n    }\n}()\ntriedb.Commit(...)","preventionTips":["Drive pathdb only through its public Commit/Checkpoint/Flush APIs under one lock","Never invoke buffer.flush from fork code; it is lifecycle-internal","If you need concurrent commits, serialize at the triedb level and test with -race"],"tags":["pathdb","triedb","concurrency","flush","panic"],"backgroundTag":null,"analyzedSha":"6bb0588ad8e7f922e4ad5580f51265a4097af08f","analyzedAt":"2026-08-15T10:06:53.996Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}