thedotmack/claude-mem · error · Error

Invalid transcript watch config: ${resolvedPath}

Error message

Invalid transcript watch config: ${resolvedPath}

What it means

Thrown by loadTranscriptWatchConfig when the config file exists and parses as JSON but is missing the required `version` or `watches` field. The loader requires both keys to consider the config valid; their absence means the file is structurally incomplete.

Source

Thrown at src/services/transcripts/config.ts:72

}

export function expandHomePath(inputPath: string): string {
  if (!inputPath) return inputPath;
  if (inputPath.startsWith('~')) {
    return join(homedir(), inputPath.slice(1));
  }
  return 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 d768ba3643)

Solutions

  1. Open the resolved path and add the missing top-level fields: `version` (number, currently 1) and `watches` (array, possibly empty).
  2. If unsure of the shape, back up the bad file and regenerate via writeSampleConfig() then re-apply your custom watches.
  3. Validate the JSON with a linter to rule out a silent parse into the wrong structure.
  4. Upgrade-stale configs: migrate old fields into the current version/watches schema.

Example fix

// before: { "stateFile": "/..." }  // missing version + watches -> throws
// after:  { "version": 1, "watches": [], "stateFile": "/..." }
Defensive patterns

Strategy: validation

Validate before calling

function isValidConfig(c: unknown): c is TranscriptWatchConfig {
  return typeof c === 'object' && c !== null
    && typeof (c as any).version === 'number'
    && Array.isArray((c as any).watches);
}
// usage:
const parsed = JSON.parse(readFileSync(resolved, 'utf-8'));
if (!isValidConfig(parsed)) throw new Error('config missing version or watches');

Type guard

function isTranscriptWatchConfig(c: unknown): c is TranscriptWatchConfig {
  if (typeof c !== 'object' || c === null) return false;
  const o = c as Record<string, unknown>;
  return typeof o.version === 'number' && Array.isArray(o.watches);
}

Try / catch

try { config = loadTranscriptWatchConfig(path); }
catch (e) {
  if (e instanceof Error && e.message.startsWith('Invalid transcript watch config')) {
    // back up and regenerate
    writeSampleConfig(path); config = loadTranscriptWatchConfig(path); return;
  }
  throw e;
}

Prevention

When it happens

Trigger: loadTranscriptWatchConfig: existsSync passes, readFileSync + JSON.parse succeeds, but `parsed.version` or `parsed.watches` is falsy. Note JSON.parse throwing a SyntaxError would surface as a separate unhandled parse error before this check.

Common situations: Hand-edited config with a typo (e.g. `watch` instead of `watches`), a partial/old config schema from a prior version that lacked `watches`, an empty `{}` file, or a file overwritten by another tool. The message names the resolved path for inspection.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/8d23d7534fb7730e. Report an issue: GitHub.