Skip to content

fix(memory): add watcher error handling and non-zero default intervalMinutes#42042

Closed
nxmxbbd wants to merge 5 commits into
openclaw:mainfrom
nxmxbbd:fix/memory-watcher-resilience
Closed

fix(memory): add watcher error handling and non-zero default intervalMinutes#42042
nxmxbbd wants to merge 5 commits into
openclaw:mainfrom
nxmxbbd:fix/memory-watcher-resilience

Conversation

@nxmxbbd

@nxmxbbd nxmxbbd commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #40088 — the chokidar file watcher can silently stop firing events after long gateway uptime, leaving the memory index stale with no fallback mechanism.

Changes

1. Default intervalMinutes from 0 → 15

File: src/agents/memory-search.ts

When the watcher fails silently, intervalMinutes: 0 (the current default) means there is no periodic fallback. Changing to 15 ensures all users have a safety net. Users who explicitly set intervalMinutes: 0 still get no interval sync (opt-out respected).

2. Mark dirty before interval sync

File: src/memory/manager-sync-ops.ts (ensureIntervalSync)

The interval timer calls this.sync({ reason: "interval" }) without force. Inside runSync, shouldSyncMemory depends on this.dirty, which is only set by the watcher's markDirty callback. If the watcher has died silently, dirty stays false and the interval sync skips memory files entirely — making it a no-op.

Setting this.dirty = true before each interval tick ensures syncMemoryFiles actually runs, listing files and comparing hashes. Unchanged files are skipped (hash match at line 740), so the overhead is negligible (~50ms for a typical workspace).

Why dirty = true instead of force: true? The force flag in runSync triggers needsFullReindex (line 1000), which does a full atomic reindex — temp DB swap, re-embed everything. That is way too heavy for a periodic safety net. Setting dirty = true only makes shouldSyncMemory pass the gate (line 1029), then syncMemoryFiles does a lightweight hash comparison and skips unchanged files.

3. Add watcher.on('error') handler

File: src/memory/manager-sync-ops.ts (ensureWatcher)

chokidar's FSWatcher extends Node's EventEmitter. Without an error listener, errors like ENOSPC (inotify limit reached) propagate as unhandled exceptions — hitting the global uncaughtException handler which calls process.exit(1), crashing the gateway. The new handler logs the error via the memory subsystem logger.

Tests

  • memory-search.test.ts: Default intervalMinutes is 15; explicit overrides (including 0) respected
  • manager.watcher-config.test.ts: Watcher registers an error event handler; interval sync marks dirty when watcher has stopped firing (fake timer test with sync spy)

All existing memory tests pass.

Alternatives considered

Alternative Why rejected
sync({ force: true }) in interval callback force triggers needsFullReindex → full atomic reindex (temp DB swap + re-embed everything). Too heavy for a 15-minute safety net.
Only default to 15 when watch: true Undermines the safety net — if watch is disabled because chokidar is broken, those users lose the fallback too.
Watcher liveness probe (heartbeat file) Deferred as potential follow-up; interval + dirty covers the core failure mode with less complexity.
Restart watcher on error Risk of resource leaks from partially-closed watchers.

Bot review notes

Codex raised three P2 concerns across three review rounds, all addressed:

  1. Default 15 affects manual-only-sync configs — Intentional. The edge case (all three sync triggers disabled + no explicit intervalMinutes) is extreme; those users can set intervalMinutes: 0. Even when the interval fires, unchanged files are skipped via hash comparison.
  2. Multimodal files cause IO spikes — Pre-existing behavior for all sync triggers. A stat-based fast path in buildFileEntry would help but is out of scope. Multimodal memory is opt-in and off by default.
  3. Cached managers accumulate interval timers — Pre-existing property of the manager cache. Default users have 1 manager. A cache eviction policy is an independent concern.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Mar 10, 2026
@greptile-apps

greptile-apps Bot commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a silent-watcher-death scenario where the chokidar file watcher could stop firing events after long gateway uptime, leaving the memory index stale indefinitely. Three targeted changes address the root cause and add defense-in-depth:

  • Default intervalMinutes: 0 → 15 (src/agents/memory-search.ts) — ensures all users get a periodic fallback sync rather than relying entirely on the watcher. Explicit 0 still opts out.
  • this.dirty = true before each interval tick (ensureIntervalSync) — without this the interval sync was a no-op when the watcher had died, because runSync's shouldSyncMemory check gates on this.dirty. Setting it before calling sync() guarantees file listing and hash comparison actually run; unchanged files are skipped via hash equality so overhead is minimal.
  • watcher.on("error") handler (ensureWatcher) — prevents unhandled-exception crashes (e.g. ENOSPC hitting inotify limits) by catching and logging watcher errors instead of propagating them to the global uncaughtException handler.

The logic is sound: sync() in manager.ts serializes concurrent calls via a this.syncing guard, so setting this.dirty = true before calling sync() is safe — if a sync is already in progress, the dirty flag persists until the next sync run picks it up. Test coverage is appropriate for all three changes.

Minor note: the watcher error handler uses err instanceof Error ? err.message : String(err) which is inconsistent with the established String(err) pattern elsewhere in the file and loses useful error properties for debugging.

Confidence Score: 4/5

  • This PR is safe to merge; all three core changes are correct and well-tested, with a minor logging consistency improvement available.
  • All three changes address a real production issue with correct implementations. The dirty-flag approach integrates properly with the existing sync() serialization guard. The watcher error handler correctly prevents unhandled exceptions. The only finding is a style inconsistency in error serialization — the new handler uses err.message while the established pattern in the file is String(err), which would better preserve error codes and stack traces for filesystem errors. This is not a correctness issue and does not block merging, but addressing it would improve production debuggability.
  • src/memory/manager-sync-ops.ts — minor logging style improvement suggested in the error handler (line 410-413)

Last reviewed commit: 58527b1

Comment thread extensions/memory-core/src/memory/manager-sync-ops.ts
@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch 2 times, most recently from 0239bea to a147f2e Compare March 12, 2026 06:32
@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch from a147f2e to f607e30 Compare March 19, 2026 02:54

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f607e306fb

ℹ️ 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".

Comment thread src/agents/memory-search.ts Outdated
@@ -239,7 +239,7 @@ function mergeConfig(
overrides?.sync?.watchDebounceMs ??
defaults?.sync?.watchDebounceMs ??
DEFAULT_WATCH_DEBOUNCE_MS,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 0,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 15,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve manual-only sync configs when defaulting intervalMinutes

Setting intervalMinutes to 15 here changes existing configs that already turned off the other automatic sync triggers (onSessionStart, onSearch, and watch) into background reindexing configs. MemoryManagerSyncOps.ensureIntervalSync() still schedules sync({ reason: "interval" }) regardless of those flags, so deployments that disabled auto-sync to avoid periodic CPU/embedding traffic will now start scanning every 15 minutes unless they discover and add a new intervalMinutes: 0 override.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging. This is intentional --- the whole point of this PR is to provide a safety net when the chokidar watcher silently dies.

A few notes:

  • The scenario (all three sync triggers disabled + no explicit intervalMinutes) is an extreme edge case. Users who go that far in disabling sync are almost certainly also setting intervalMinutes: 0 explicitly.
  • Even when the interval fires, syncMemoryFiles compares file hashes and skips unchanged files (line 740), so there is no re-embedding cost --- just a lightweight file listing + hash check.
  • Users who truly want zero background sync can still set intervalMinutes: 0 to opt out.

Adding conditional logic (e.g. only default to 15 when watch: true) would undermine the safety net --- if watch is disabled precisely because chokidar is broken, those users lose the fallback too.

@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch from f607e30 to 1fc0af6 Compare March 20, 2026 04:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1fc0af6c73

ℹ️ 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".

Comment thread src/agents/memory-search.ts Outdated
@@ -239,7 +239,7 @@ function mergeConfig(
overrides?.sync?.watchDebounceMs ??
defaults?.sync?.watchDebounceMs ??
DEFAULT_WATCH_DEBOUNCE_MS,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 0,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 15,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid default interval rescans for multimodal memory files

When intervalMinutes is omitted, this now enables a 15-minute background sync for every memory index. In that sync path, ensureIntervalSync() marks the manager dirty and syncMemoryFiles() rebuilds buildFileEntry() for every memory asset before it can decide the hash is unchanged (src/memory/manager-sync-ops.ts:715-749). For multimodal memory, buildFileEntry() rereads the entire file to compute dataHash (src/memory/internal.ts:205-243), so a workspace with several 5-10 MB images/audio notes will now incur recurring full-blob IO and RSS spikes every 15 minutes even when nothing changed. That is a meaningful performance regression for users who only took the default.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging. The IO pattern you describe is accurate, but it is pre-existing behavior --- every sync trigger (watch, onSessionStart, onSearch) goes through the same syncMemoryFilesbuildFileEntryreadFile path. This PR does not change how sync works; it only adds an interval trigger as a fallback for when the watcher silently dies.

A stat-based fast path in buildFileEntry (skip readFile when mtime+size are unchanged) would be a worthwhile optimization, but that applies to all sync triggers equally and is out of scope for this fix.

Also worth noting: multimodal memory is opt-in and off by default, so the default-config user this comment is concerned about would not have large binary files in their memory directory in the first place.

@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch from 1fc0af6 to 0608836 Compare March 20, 2026 05:43
@openclaw-barnacle openclaw-barnacle Bot added app: macos App: macos size: M and removed size: S labels Mar 20, 2026
@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch from d0e8837 to 0eb69f9 Compare March 20, 2026 05:53
@openclaw-barnacle openclaw-barnacle Bot added size: S and removed app: macos App: macos size: M labels Mar 20, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0eb69f9b7f

ℹ️ 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".

Comment thread src/agents/memory-search.ts Outdated
@@ -239,7 +239,7 @@ function mergeConfig(
overrides?.sync?.watchDebounceMs ??
defaults?.sync?.watchDebounceMs ??
DEFAULT_WATCH_DEBOUNCE_MS,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 0,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 15,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid enabling 15-minute rescans on permanently cached managers

This default is broader than a one-shot safety net because builtin managers are cached globally in src/memory/manager.ts:164-198 and most long-lived callsites never close them (for example src/agents/tools/memory-tool.ts:96-127 and src/agents/pi-embedded-runner/compact.ts:323-334). In a multi-agent gateway—or after any config change that produces a new cache key—each touched manager will now keep its own 15-minute interval sync loop forever, so background workspace scans accumulate across dormant managers instead of only the active one.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed analysis. The manager cache accumulation you describe is technically accurate — cached managers aren't evicted and each keeps its own interval timer.

However, this is a pre-existing property of the manager cache, not something introduced by this PR. Any intervalMinutes > 0 value (whether user-configured or defaulted) behaves identically. The fix here is adding a cache eviction / idle-cleanup policy to the manager cache itself, which is an independent concern and shouldn't block a safety-net default for silent watcher death.

In practice: default users have 1 manager (single agent, single workspace, same settings = same cache key). Even in multi-agent gateways, the number of distinct cache keys is small, and the interval sync is lightweight (stat + hash compare, unchanged files skipped).

@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch 2 times, most recently from 0f58389 to d71d266 Compare March 24, 2026 17:54
@nxmxbbd

nxmxbbd commented Mar 24, 2026

Copy link
Copy Markdown
Contributor Author

CI note

Current CI failures (check oxfmt, build-smoke RSS, check-additional plugin-sdk-api-drift) are regressions on main — they reproduce on upstream's own latest CI run (#23504073274, commit 488ad4ac). No failures are related to this PR's changes.

@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch 3 times, most recently from e912434 to 1595a1b Compare March 25, 2026 06:35
@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch from 1595a1b to e7d30e1 Compare April 1, 2026 01:16
@openclaw-barnacle openclaw-barnacle Bot added the extensions: memory-core Extension: memory-core label Apr 1, 2026
@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch from e7d30e1 to 98361af Compare April 1, 2026 01:26
@nxmxbbd

nxmxbbd commented Apr 1, 2026

Copy link
Copy Markdown
Contributor Author

CI note (rebase 2026-04-01)

Rebased onto d2663262d4. The 3 current CI failures are not related to this PR:

  • check (oxlint): 3x no-base-to-string errors in src/shared/chat-content.ts and src/agents/pi-embedded-subscribe.handlers.messages.ts — pre-existing on main, caused by String(value) on unknown type in coerceText() helpers
  • check-additional: no-extension-test-core-imports lint — pre-existing on main
  • checks-node-test-4 / checks-windows-node-test-1: infrastructure failures (test runner crash, not test failures)

All reproduce on upstream's own latest CI run on main.

@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch from 98361af to a1cbf70 Compare April 4, 2026 18:23
@nxmxbbd

nxmxbbd commented Apr 6, 2026

Copy link
Copy Markdown
Contributor Author

CI note (rebase 2026-04-06)

Rebased onto 1a270f81c3. The current CI failures are not related to this PR:

  • checks-fast-extensions-shard-{2,3,6}: upstream flaky tests (same shards fail on recently merged fix: preserve explicit node routing under elevated auto exec #61739)
  • checks-fast-contracts-protocol: runtime-seams.contract.test.ts fetch guard failure (pre-existing)
  • extension-fast-memory-core: fts-only-reindex, atomic-reindex, session-reindex, short-term-promotion — none of these files are touched by this PR
  • build-smoke: downstream of the above failures

Our changed files (manager-sync-ops.ts, memory-search.ts) and added tests pass. Core checks (check, check-additional, build-artifacts, checks-fast-bundled) all green.

@nxmxbbd

nxmxbbd commented Apr 6, 2026

Copy link
Copy Markdown
Contributor Author

@obviyus Related to the above --- adds error recovery for the memory file watcher (chokidar silently dies, memory search stops working). All bot feedback addressed.

@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch 2 times, most recently from 91a64cc to 58f2c1c Compare April 7, 2026 12:54

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58f2c1cc6d

ℹ️ 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".

Comment thread src/agents/memory-search.ts Outdated
@@ -233,7 +233,7 @@ function mergeConfig(
overrides?.sync?.watchDebounceMs ??
defaults?.sync?.watchDebounceMs ??
DEFAULT_WATCH_DEBOUNCE_MS,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 0,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 15,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep sync resolver default aligned with interval fallback

Changing mergeConfig() to default intervalMinutes to 15 here, while resolveSyncConfig() (used by resolveMemorySearchSyncConfig) still defaults to 0, introduces two conflicting defaults for the same setting. That divergence means callers using the sync-only resolver can still behave as if interval fallback is disabled even when the main memory config resolver enables it, which undermines the safety-net behavior this commit is introducing. Please centralize or mirror the default so both resolvers produce the same value when intervalMinutes is omitted.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 29a4a1ab — resolveSyncConfig now defaults intervalMinutes to 15, matching mergeConfig. Good catch.

@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch from 58f2c1c to 47c486f Compare April 7, 2026 13:03

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 47c486f260

ℹ️ 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".

Comment thread src/agents/memory-search.ts Outdated
@@ -233,7 +233,7 @@ function mergeConfig(
overrides?.sync?.watchDebounceMs ??
defaults?.sync?.watchDebounceMs ??
DEFAULT_WATCH_DEBOUNCE_MS,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 0,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 15,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep sync defaults consistent across memory config resolvers

This change updates mergeConfig() to default sync.intervalMinutes to 15, but resolveSyncConfig() (used by resolveMemorySearchSyncConfig) still defaults it to 0, so two public resolvers now disagree for the same omitted config. Callers that depend on the sync-only resolver (for example extensions/memory-core/src/memory/qmd-manager.ts) will continue behaving as if interval fallback is disabled, which creates inconsistent runtime behavior and makes the new default safety net apply only on one code path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same fix as above — aligned in 29a4a1ab.

@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch 3 times, most recently from cb80de9 to fb66698 Compare April 8, 2026 06:53
@nxmxbbd

nxmxbbd commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 9a4f6edbd6 - updated extractMemoryErrorReason to use formatErrorMessage() for consistent error handling pattern with isMemoryReadonlyDbError function.

@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch 6 times, most recently from 145351b to 4341d80 Compare April 8, 2026 13:25

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4341d800c7

ℹ️ 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".

@@ -358,7 +358,7 @@ function resolveSyncConfig(
overrides?.sync?.watchDebounceMs ??
defaults?.sync?.watchDebounceMs ??
DEFAULT_WATCH_DEBOUNCE_MS,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 0,
intervalMinutes: overrides?.sync?.intervalMinutes ?? defaults?.sync?.intervalMinutes ?? 15,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip interval fallback when memory source is disabled

Changing the omitted sync.intervalMinutes default to 15 enables periodic sync for configurations that only index sessions (sources: ["sessions"]) even though there is no file watcher to recover. ensureIntervalSync() still schedules sync({ reason: "interval" }) regardless of sources, so these setups now run background sync cycles (provider/vector preflight + metadata checks) every 15 minutes when they previously stayed idle unless session triggers fired. This is an unintended workload increase for session-only deployments and can produce periodic noise despite no memory-file source being enabled.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0032db90ab - added check to skip interval timer setup when memory files are not in configured sources. Session-only deployments now avoid the unnecessary background sync cycles.

nxmxbbd added 4 commits April 8, 2026 21:58
Adds a test that simulates a dead watcher scenario: after dirty is
cleared by a completed sync, the interval callback must set dirty=true
before calling sync() so that syncMemoryFiles actually runs. Without
this, interval sync is a no-op when the watcher has silently died.

Uses fake timers to advance past the interval tick and a sync spy to
confirm both the dirty flag and the sync invocation.
…rorReason

Use formatErrorMessage() instead of instanceof Error check to align with the
error handling pattern used elsewhere in the file (isMemoryReadonlyDbError)
@nxmxbbd
nxmxbbd force-pushed the fix/memory-watcher-resilience branch from 0032db9 to ccf479c Compare April 8, 2026 13:58
@nxmxbbd

nxmxbbd commented Apr 13, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #65755 — clean squash onto latest main with identical changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling extensions: memory-core Extension: memory-core size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

memory_search: chokidar file watcher silently stops delivering events, index goes stale

1 participant