{"record":{"id":"f189020563cd56e2","repo":"benbjohnson/litestream","slug":"store-required","errorCode":null,"errorMessage":"store required","messagePattern":"store required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/litestream/directory_watcher.go","lineNumber":52,"sourceCode":"\n\tmu          sync.Mutex\n\tdbs         map[string]*litestream.DB\n\twatchedDirs map[string]struct{}\n\n\t// Only accessed from the run() goroutine, so no mutex is needed.\n\tpendingEvents  map[string]fsnotify.Op\n\tdebounceActive bool\n\n\twg sync.WaitGroup\n}\n\n// NewDirectoryMonitor returns a new monitor for directory-based replication.\nfunc NewDirectoryMonitor(ctx context.Context, store *litestream.Store, dbc *DBConfig, existing []*litestream.DB) (*DirectoryMonitor, error) {\n\tif dbc == nil {\n\t\treturn nil, errors.New(\"database config required\")\n\t}\n\tif store == nil {\n\t\treturn nil, errors.New(\"store required\")\n\t}\n\n\tdirPath, err := expand(dbc.Dir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif _, err := os.Stat(dirPath); err != nil {\n\t\treturn nil, err\n\t}\n\n\twatcher, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmonitorCtx, cancel := context.WithCancel(ctx)\n\tdm := &DirectoryMonitor{","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/cmd/litestream/directory_watcher.go#L34-L70","documentation":"NewDirectoryMonitor requires a non-nil *litestream.Store to attach the monitored databases to. This error is returned when the store parameter is nil, because the monitor has nowhere to register newly discovered databases. Like the dbc check, it is a constructor guard against invalid arguments.","triggerScenarios":"Calling litestream.NewDirectoryMonitor(ctx, nil, dbc, existing) — typically when Store initialization failed earlier or was skipped, and the nil store was still passed to the monitor constructor.","commonSituations":"Embedding litestream where NewStore/Open failed or returned nil and the error was ignored; starting the directory monitor before the store is created; refactoring that reordered store creation after monitor startup.","solutions":["Create and open the *litestream.Store before calling NewDirectoryMonitor, and pass the resulting pointer.","Check the error/return of the code that built the store — a nil store usually means an earlier failure was swallowed.","If store creation failed, abort startup instead of continuing with a nil store."],"exampleFix":"// before\nstore, err := litestream.NewStore(cfg) // err ignored\nmon, err := NewDirectoryMonitor(ctx, store, dbc, existing)\n// after\nstore, err := litestream.NewStore(cfg)\nif err != nil {\n    return err\n}\nif err := store.Open(); err != nil {\n    return err\n}\nmon, err := NewDirectoryMonitor(ctx, store, dbc, existing)","handlingStrategy":"type-guard","validationCode":"if store == nil {\n    return errors.New(\"store must be created and opened before starting the directory monitor\")\n}","typeGuard":"func storeReady(s *litestream.Store) bool { return s != nil }","tryCatchPattern":"mon, err := litestream.NewDirectoryMonitor(ctx, store, dbc, existing)\nif err != nil {\n    if err.Error() == \"store required\" {\n        return fmt.Errorf(\"store not initialized: %w\", err)\n    }\n    return err\n}","preventionTips":["Establish a fixed startup order: create store, open it, then create monitors.","Propagate (don't swallow) the error from store creation so a nil store never flows onward.","Add an assertion/log at startup when store is nil."],"tags":["go","configuration","nil-argument","store"],"backgroundTag":"null-argument","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}