thedotmack/claude-mem · warning
Failed to parse files JSON in hasDirectChildFile
Error message
Failed to parse files JSON in hasDirectChildFile
What it means
hasDirectChildFile parses a JSON array of file paths stored in an observation (files_modified or files_read) and checks whether any is a direct child of a folder. When the stored string is not valid JSON, JSON.parse throws inside checkFiles and this warning is logged; the function safely returns false, meaning the folder is treated as having no direct-child files.
Solutions
- Inspect the offending observation row (obs.files_modified / obs.files_read) and fix or re-write it as a proper JSON array of path strings
- Delete or null out the malformed observation so it is skipped
- Add a writer-side guard that always JSON.stringify the array before persisting
- Migrate old rows with a one-time script that re-serializes or clears non-JSON values
Example fix
// before obs.files_modified = 'src/a.ts, src/b.ts'; // after obs.files_modified = JSON.stringify(['src/a.ts', 'src/b.ts']);
Defensive patterns
Strategy: validation
Validate before calling
function isPathArray(v) {
if (typeof v !== 'string') return false;
try { const p = JSON.parse(v); return Array.isArray(p) && p.every(x => typeof x === 'string'); }
catch { return false; }
}
// call hasDirectChildFile only when isPathArray(obs.files_modified) Type guard
function isFilesJson(v: unknown): v is string { return typeof v === 'string' && v.trimStart().startsWith('['); } Try / catch
try {
const files = JSON.parse(filesJson);
if (Array.isArray(files)) return files.some(f => isDirectChild(f, folderPath));
} catch { return false; } // treat malformed as 'no match' Prevention
- Always JSON.stringify arrays before persisting observation fields
- Store NULL, not '', when there are no files
- Add a DB migration normalizing legacy non-JSON values
- Log the raw value on parse failure to spot writer bugs early
When it happens
Trigger: Calling hasDirectChildFile with an observation whose files_modified or files_read field is malformed — e.g. a bare path string instead of a JSON array ('src/foo.ts' rather than '["src/foo.ts"]'), truncated JSON, single quotes, or non-JSON text written by an older/buggy writer version.
Common situations: Legacy database rows written before the JSON-array convention was adopted; manual edits to the claude-mem SQLite DB; partial writes from a crashed process leaving truncated JSON; data imported from another tool.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse files_modified JSON
- Failed to parse files_read JSON
- sync hub status: response is not JSON
- Anthropic API error
- canonical content
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/5c8ade3ba1dff233.
Report an issue: GitHub.
Appendix: source
Thrown at src/cli/claude-md-commands.ts:128
const fullPath = path.join(dir, entry.name);
folders.add(fullPath);
walkDirectoriesWithIgnore(fullPath, folders, depth + 1);
}
} catch {
// Ignore permission errors
}
}
function hasDirectChildFile(obs: ObservationRow, folderPath: string): boolean {
const checkFiles = (filesJson: string | null): boolean => {
if (!filesJson) return false;
try {
const files = JSON.parse(filesJson);
if (Array.isArray(files)) {
return files.some(f => isDirectChild(f, folderPath));
}
} catch (error) {
logger.warn('CLAUDE_MD', 'Failed to parse files JSON in hasDirectChildFile', { error: error instanceof Error ? error.message : String(error) });
}
return false;
};
return checkFiles(obs.files_modified) || checkFiles(obs.files_read);
}
function findObservationsByFolder(db: Database, relativeFolderPath: string, project: string, limit: number): ObservationRow[] {
const queryLimit = limit * 3;
const sql = `
SELECT o.*, o.discovery_tokens
FROM observations o
WHERE o.project = ?
AND (o.files_modified LIKE ? OR o.files_read LIKE ?)
ORDER BY o.created_at_epoch DESC
LIMIT ?
`;View on GitHub (pinned to d8bc9755e7)