fix(memory): add watcher error handling and non-zero default intervalMinutes#65755
fix(memory): add watcher error handling and non-zero default intervalMinutes#65755nxmxbbd wants to merge 3 commits into
Conversation
…Minutes The chokidar file watcher can silently stop firing events after long gateway uptime, leaving the memory index permanently stale. This change: - Registers an error handler on the watcher so errors are logged instead of crashing the process - Sets dirty=true in the interval sync callback so syncMemoryFiles actually re-reads files even when the watcher has died - Skips interval sync for session-only configs that don't use memory files - Defaults intervalMinutes to 15 (was 0/disabled) so interval sync acts as a reliable fallback when the watcher fails - Uses formatErrorMessage() in extractMemoryErrorReason for consistent error serialization (captures error codes, not just message)
Greptile SummaryThis PR fixes three reliability gaps in the memory-core watcher: registers an
Confidence Score: 4/5Safe to merge after addressing the One P2 logic issue: extensions/memory-core/src/memory/manager-sync-ops.ts — interval callback in Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/memory-core/src/memory/manager-sync-ops.ts
Line: 629-634
Comment:
**`dirty` permanently stuck `true` for session-only configs**
`dirty` is the memory-files dirty flag — it's only cleared at `this.dirty = false` inside `if (shouldSyncMemory)`, which requires `this.sources.has("memory")`. For a config where `sources` has `"sessions"` but not `"memory"`, the interval sets `dirty = true` on every tick but nothing ever clears it, so `dirty` stays `true` for the lifetime of the manager. Downstream effects: `status()` always reports `dirty: true`, and search-triggered syncs (via `startAsyncSearchSync`) see `dirty: true` and invoke `sync()` on every query, even though those calls are no-ops for memory.
Guard the flag assignment with a sources check:
```suggestion
if (this.sources.has("memory")) {
this.dirty = true;
}
runDetachedMemorySync(() => this.sync({ reason: "interval" }), "interval");
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (2): Last reviewed commit: "fix: preserve interval sync for sessions..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7d319dec43
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // Skip interval sync when memory files are not enabled - session-only configs | ||
| // don't need periodic fallback since sessions have their own sync triggers | ||
| if (!this.sources.has("memory")) { | ||
| return; |
There was a problem hiding this comment.
Preserve interval sync for sessions-only configurations
The new early return in ensureIntervalSync drops the interval timer whenever sources does not include "memory", so a config that indexes only "sessions" now ignores sync.intervalMinutes entirely. Before this change, interval ticks retried pending session sync work (for example after a transient sync failure or when a session-delta trigger was coalesced behind an in-flight sync); now those retries never happen unless another trigger occurs, which can leave session memory stale indefinitely in low-activity agents.
Useful? React with 👍 / 👎.
- Remove unreachable fallback code in extractMemoryErrorReason after formatErrorMessage
- Remove redundant sources.has('memory') guard in interval callback
Fixes P2 suggestions from Greptile review.
|
Fixed both P2 suggestions in 5ba3fa7: ✅ Removed dead fallback code in Both suggestions were valid - |
Address Codex bot feedback: sessions-only configs can benefit from interval sync retries when session sync fails or is coalesced. The sync logic already handles sessions correctly via shouldSyncSessionsForReindex.
|
@chatgpt-codex-connector Fixed in afaf869. You're right that sessions-only configs should retain interval sync for retry purposes. Changed the condition to allow interval sync when either memory files OR sessions are enabled, so session sync failures can be retried via the interval fallback mechanism. |
|
To use Codex here, create a Codex account and connect to github. |
|
@greptile-apps Fixed the redundant @chatgpt-codex-connector Fixed the sessions-only config issue in afaf869 — changed condition to |
|
To use Codex here, create a Codex account and connect to github. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: afaf8692b2
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // Mark dirty so runSync actually checks for file changes. | ||
| // Without this, interval sync is a no-op when the watcher has silently | ||
| // stopped firing events — dirty stays false and syncMemoryFiles is skipped. | ||
| this.dirty = true; |
There was a problem hiding this comment.
Set dirty only when memory source is enabled
This interval callback marks dirty unconditionally, which regresses session-only configurations (sources: ["sessions"]). In that mode runSync never enters the memory branch that clears dirty, so once the first interval fires the manager stays permanently dirty; then startAsyncSearchSync treats every search as needing a sync and repeatedly schedules no-op sync work, and status output remains inaccurately dirty even when there are no pending session deltas.
Useful? React with 👍 / 👎.
Problem
The chokidar file watcher used by memory-core can silently stop firing events after long gateway uptime. When this happens, the memory index becomes permanently stale —
dirtyis never set totrue, sosyncMemoryFilesis never called, and memory search returns outdated results indefinitely.Additionally,
intervalMinutesdefaults to 0 (disabled), so there is no fallback mechanism when the watcher dies.Root Cause
sync()butdirtystaysfalse, sosyncMemoryFilesis skipped entirelyerr.message, missing structured fields likecode: 'SQLITE_READONLY'Fix
errorhandler on the chokidar watcher (logs warning instead of crashing)dirty = truein the interval sync callback before callingsync(), so file changes are detected even when the watcher has diedintervalMinutesto 15 so interval sync acts as a reliable fallbackformatErrorMessage()inextractMemoryErrorReasonfor consistent error serializationTests
manager.watcher-config.test.ts: verifies interval sync setsdirty = truewhen watcher has stopped, and that an error handler is registeredmemory-search.test.ts: verifiesintervalMinutesdefaults to 15, respects explicit overrides, and respects explicit 0manager.readonly-recovery.test.ts: updated to expect JSON-serialized error formatSupersedes #42042 (clean rewrite, same fix).