{"record":{"id":"89b3d614a5f6bde3","repo":"wavetermdev/waveterm","slug":"error-flushing-cache-entry-v-v","errorCode":null,"errorMessage":"error flushing cache entry[%v]: %v","messagePattern":"error flushing cache entry\\[(.+?)\\]: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/filestore/blockstore.go","lineNumber":419,"sourceCode":"\tdefer s.setIsFlushing(false)\n\tstartTime := time.Now()\n\tdefer func() {\n\t\tstats.FlushDuration = time.Since(startTime)\n\t}()\n\n\t// get a copy of dirty keys so we can iterate without the lock\n\tdirtyCacheKeys := s.getDirtyCacheKeys()\n\tstats.NumDirtyEntries = len(dirtyCacheKeys)\n\tfor _, key := range dirtyCacheKeys {\n\t\terr := withLock(s, key.ZoneId, key.Name, func(entry *CacheEntry) error {\n\t\t\treturn entry.flushToDB(ctx, false)\n\t\t})\n\t\tif ctx.Err() != nil {\n\t\t\t// transient error (also must stop the loop)\n\t\t\treturn stats, ctx.Err()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn stats, fmt.Errorf(\"error flushing cache entry[%v]: %v\", key, err)\n\t\t}\n\t\tstats.NumCommitted++\n\t}\n\treturn stats, nil\n}\n\n///////////////////////////////////\n\nfunc (f *WaveFile) partIdxAtOffset(offset int64) int {\n\tpartIdx := int(offset / partDataSize)\n\tif f.Opts.Circular {\n\t\tmaxPart := int(f.Opts.MaxSize / partDataSize)\n\t\tpartIdx = partIdx % maxPart\n\t}\n\treturn partIdx\n}\n\nfunc incompletePartsFromMap(partMap map[int]int) []int {","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/filestore/blockstore.go#L401-L437","documentation":"During FlushCache, a per-entry flush (writing a dirty cache entry's parts/metadata to the DB) failed for a specific key. FlushCache wraps the underlying error with the entry key so the caller knows which file entry failed; entries flushed before the failure still count in stats.NumCommitted.","triggerScenarios":"Any underlying error from flushing a single cache entry during FlushCache — DB write failures, context cancellation on the entry's operation, corrupted part data — surfaced as 'error flushing cache entry[zone:name]: <cause>'.","commonSituations":"Database unavailable or read-only during a flush; a large flush racing a shutdown so entries get canceled contexts; disk/DB quota issues while persisting data parts.","solutions":["Inspect the wrapped cause (%v of the inner error) to find the real failure (DB connectivity, permissions, etc.).","Retry FlushCache once the underlying DB issue is fixed; unflushed entries remain dirty and will be retried.","Check ctx.Err() to distinguish transient cancellation from a persistent DB error."],"exampleFix":"// before\nstats, err := fs.FlushCache(ctx)\nif err != nil { log.Fatal(err) }\n// after\nstats, err := fs.FlushCache(ctx)\nif err != nil {\n    log.Printf(\"partial flush committed=%d, err=%v; retrying\", stats.NumCommitted, err)\n    time.Sleep(time.Second)\n    _, err = fs.FlushCache(ctx)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"stats, err := fs.FlushCache(ctx)\nif err != nil {\n    log.Printf(\"flush failed after %d entries: %v\", stats.NumCommitted, err)\n    if ctx.Err() == nil { // not canceled: worth retrying\n        time.Sleep(time.Second)\n        _, err = fs.FlushCache(ctx)\n    }\n}","preventionTips":["Monitor DB health before triggering large flushes.","Avoid canceling contexts mid-flush (e.g. during shutdown); use a detached context for flushes.","Surface the wrapped inner error, not just the wrapper message."],"tags":["filestore","flush","database"],"backgroundTag":"flush-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}