Context
Surfaced in mcp__neo-mjs-memory-core__healthcheck response 2026-05-11T12:24:20Z post-PR #11200 merge:
"backup": {
"lastSuccessful": null,
"count": 0,
"error": "fs.readdir is not a function"
}
Independently corroborated by @neo-gpt's parallel healthcheck call at ~12:25Z (same backup.error payload). Non-blocker for #11181 substrate (restored-summary visibility is functionally restored via PR #11200's --apply execution at 12:20Z), but the backup-block of healthcheck is permanently broken until fixed.
The Problem
HealthService#buildBackupStateBlock (or whatever method materializes the backup healthcheck block) calls fs.readdir(...) on an object that doesn't have that method. Likely an ESM import-shape mismatch:
import fs from 'fs';
import fs from 'fs/promises';
import { promises as fs } from 'fs';
Backup-state observability is dead until this resolves — operators cannot verify backup-substrate health from MCP healthcheck.
The Architectural Reality
ai/services/memory-core/HealthService.mjs contains the #buildBackupStateBlock private method (or equivalent) that surfaces backup metadata into the healthcheck payload
- The current implementation calls
fs.readdir against a path resolved from backup-substrate config
- The error
fs.readdir is not a function indicates the runtime fs reference at the call-site is not the fs/promises namespace
- Adjacent
HealthService blocks (topology, embedding, summary providers) appear to work correctly — so the issue is isolated to backup-block import/reference
The Fix
- Locate the
fs.readdir(...) call in HealthService.mjs (likely inside #buildBackupStateBlock or #countBackupFiles helper)
- Verify the
fs import at the top of the file uses fs/promises (or await import('fs/promises') if dynamic)
- If import is already correct, the call-site may be using
fs.promises.readdir(...) against a default-import fs — fix to fs.readdir(...) directly against the fs/promises named import
- Add unit test that mocks the backup-substrate directory + asserts
healthcheck.backup returns non-error shape with valid file count
Acceptance Criteria
Out of Scope
- Refactoring
HealthService more broadly (other blocks work fine)
- Adding new backup-substrate features (just restoring observability)
- Backup execution/scheduling logic (separate concern)
Avoided Traps
- Wholesale
HealthService audit — only the backup block needs the import fix; auditing adjacent blocks would scope-creep
- Adding a fallback default for missing backup dir — graceful skip is fine; don't fabricate backup-state when none exists
Related
- PR #11200 —
fix(memory-core): share core swarm summaries (#11181) — surfaced this gap empirically via the new buildChromaMigrationStats projection running healthcheck
- 3-way cross-family V-B-A — independently corroborated by @neo-gpt + @neo-opus-4-7 healthchecks at ~12:24Z and ~12:25Z respectively
Origin Session ID
c2912891-b459-4a03-b2af-154d5e264df1
Handoff Retrieval Hints
query_raw_memories(query="HealthService backup error fs.readdir healthcheck")
- File anchor:
ai/services/memory-core/HealthService.mjs (search for fs.readdir or buildBackupStateBlock)
Context
Surfaced in
mcp__neo-mjs-memory-core__healthcheckresponse 2026-05-11T12:24:20Z post-PR #11200 merge:"backup": { "lastSuccessful": null, "count": 0, "error": "fs.readdir is not a function" }Independently corroborated by @neo-gpt's parallel healthcheck call at ~12:25Z (same
backup.errorpayload). Non-blocker for #11181 substrate (restored-summary visibility is functionally restored via PR #11200's--applyexecution at 12:20Z), but the backup-block of healthcheck is permanently broken until fixed.The Problem
HealthService#buildBackupStateBlock(or whatever method materializes thebackuphealthcheck block) callsfs.readdir(...)on an object that doesn't have that method. Likely an ESM import-shape mismatch:// Probably: import fs from 'fs'; // CommonJS-style default; readdir is on fs.promises, not fs // Should be: import fs from 'fs/promises'; // ESM canonical; readdir is on fs directly // Or: import { promises as fs } from 'fs'; // alias-styleBackup-state observability is dead until this resolves — operators cannot verify backup-substrate health from MCP healthcheck.
The Architectural Reality
ai/services/memory-core/HealthService.mjscontains the#buildBackupStateBlockprivate method (or equivalent) that surfaces backup metadata into the healthcheck payloadfs.readdiragainst a path resolved from backup-substrate configfs.readdir is not a functionindicates the runtimefsreference at the call-site is not thefs/promisesnamespaceHealthServiceblocks (topology, embedding, summary providers) appear to work correctly — so the issue is isolated to backup-block import/referenceThe Fix
fs.readdir(...)call inHealthService.mjs(likely inside#buildBackupStateBlockor#countBackupFileshelper)fsimport at the top of the file usesfs/promises(orawait import('fs/promises')if dynamic)fs.promises.readdir(...)against a default-importfs— fix tofs.readdir(...)directly against thefs/promisesnamed importhealthcheck.backupreturns non-error shape with valid file countAcceptance Criteria
HealthService#buildBackupStateBlockcallsfs.readdircorrectly (no runtime error)healthcheck.backup.erroris null when backup substrate exists + readablehealthcheck.backup.countreports correct file count from JSONL backups dirhealthcheck.backup.lastSuccessfulreports the most-recent backup's timestampOut of Scope
HealthServicemore broadly (other blocks work fine)Avoided Traps
HealthServiceaudit — only the backup block needs the import fix; auditing adjacent blocks would scope-creepRelated
fix(memory-core): share core swarm summaries (#11181)— surfaced this gap empirically via the newbuildChromaMigrationStatsprojection running healthcheckOrigin Session ID
c2912891-b459-4a03-b2af-154d5e264df1Handoff Retrieval Hints
query_raw_memories(query="HealthService backup error fs.readdir healthcheck")ai/services/memory-core/HealthService.mjs(search forfs.readdirorbuildBackupStateBlock)