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

  1. 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
  2. Delete or null out the malformed observation so it is skipped
  3. Add a writer-side guard that always JSON.stringify the array before persisting
  4. 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

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.

Related errors


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)