thedotmack/claude-mem · error
Invalid transcript watch config
Error message
Invalid transcript watch config: ${resolvedPath} What it means
After reading and JSON-parsing the watch config, loadTranscriptWatchConfig validates its shape: a truthy version and a watches object/array are required. A file that parses as JSON but lacks these fields is rejected as structurally invalid, with the resolved path in the message.
Solutions
- Add the required fields: "version": 1 and a "watches" section to the config file.
- Compare against the current documented schema and migrate an old-format config.
- Validate the JSON keys (version, watches, stateFile) after editing.
- Regenerate a fresh default config and re-apply your custom watches.
Example fix
// before
{ "stateFile": "~/.claude-mem/watch-state.json" }
// after
{ "version": 1, "watches": [{ "path": "~/.claude/projects/**/transcript.jsonl" }], "stateFile": "~/.claude-mem/watch-state.json" } Defensive patterns
Strategy: validation
Validate before calling
const raw = JSON.parse(readFileSync(p, 'utf8'));
if (typeof raw.version !== 'number' || !raw.watches) throw new Error(`Invalid watch config at ${p}`); Type guard
function isValidWatchConfig(v: unknown): v is TranscriptWatchConfig {
return typeof v === 'object' && v !== null && 'version' in v && 'watches' in v;
} Try / catch
try { const cfg = loadTranscriptWatchConfig(); } catch (e) { if (e.message.includes('Invalid transcript watch config')) { migrateOrRegenerateConfig(); } } Prevention
- Validate the config against the schema after every edit.
- Version your config schema and provide migrations.
- Keep a known-good template of the config in the repo.
When it happens
Trigger: The config file at resolvedPath exists and is valid JSON but omits version or watches — e.g. an empty object {}, an outdated schema, or a file containing only stateFile.
Common situations: Hand-edited config missing required keys; config created by an older plugin version before schema changes; partial write/truncation that still left valid JSON; copying a template but not filling it in.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- CLAUDE_MEM_TELEGRAM_WRAPUP_ROUTES must be a JSON object
- Invalid , using default
- schema v46: . row is not a positive canonical uint64…
- ServerGenerationJobPayloadValidationError
- sync hub status: response must be an object
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/8d23d7534fb7730e.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/transcripts/config.ts:73
export function expandHomePath(inputPath: string): string {
if (!inputPath) return inputPath;
// Shared expandTilde/expandHome leave `~user/...` alone (resolving another
// user's home is out of scope). The old inline version sliced one character
// off any leading tilde, so `~alice/transcripts` was rewritten to
// `<home>/alice/transcripts` and the watcher ingested nothing silently.
return expandTilde(inputPath);
}
export function loadTranscriptWatchConfig(path = DEFAULT_CONFIG_PATH): TranscriptWatchConfig {
const resolvedPath = expandHomePath(path);
if (!existsSync(resolvedPath)) {
throw new Error(`Transcript watch config not found: ${resolvedPath}`);
}
const raw = readFileSync(resolvedPath, 'utf-8');
const parsed = JSON.parse(raw) as TranscriptWatchConfig;
if (!parsed.version || !parsed.watches) {
throw new Error(`Invalid transcript watch config: ${resolvedPath}`);
}
if (!parsed.stateFile) {
parsed.stateFile = DEFAULT_STATE_PATH;
}
return parsed;
}
export function writeSampleConfig(path = DEFAULT_CONFIG_PATH): void {
const resolvedPath = expandHomePath(path);
const dir = dirname(resolvedPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
writeFileSync(resolvedPath, JSON.stringify(SAMPLE_CONFIG, null, 2));
}
View on GitHub (pinned to d8bc9755e7)