thedotmack/claude-mem · warning

Failed to disable Codex transcript AGENTS.md context

Error message

Failed to disable Codex transcript AGENTS.md context

What it means

The codex integration also removes legacy AGENTS.md context references from a transcript-watch JSON config. stripLegacyTranscriptWatchContexts parses the config, filters watches whose context points at CODEX_AGENTS_MD_PATH, and rewrites the file; any throw — JSON.parse SyntaxError, read failure, or write failure — is caught here, logged, and returned as false. Legacy watch entries then survive in the config.

Source

Thrown at src/services/integrations/CodexCliInstaller.ts:408

    && updateOn.length === 2
    && updateOn.includes('session_start')
    && updateOn.includes('session_end');
  if (!hasLegacyUpdateOn) return false;

  if (context.path === undefined) return true;
  return typeof context.path === 'string'
    && path.resolve(expandHome(context.path)) === CODEX_AGENTS_MD_PATH;
}

function disableCodexTranscriptAgentsContext(): boolean {
  if (!existsSync(CODEX_TRANSCRIPT_WATCH_CONFIG_PATH)) return true;

  try {
    stripLegacyTranscriptWatchContexts();
    return true;
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    logger.warn('WORKER', 'Failed to disable Codex transcript AGENTS.md context', { error: message });
    return false;
  }
}

function stripLegacyTranscriptWatchContexts(): void {
  const parsed = JSON.parse(readFileSync(CODEX_TRANSCRIPT_WATCH_CONFIG_PATH, 'utf-8')) as unknown;
  if (!isRecord(parsed) || !Array.isArray(parsed.watches)) return;

  let changed = false;
  for (const watch of parsed.watches) {
    if (!isRecord(watch) || !isCodexTranscriptWatch(watch)) continue;
    if (!isRecord(watch.context) || !isLegacyCodexAgentsContext(watch.context)) continue;
    delete watch.context;
    changed = true;
  }

  if (changed) {
    writeFileSync(CODEX_TRANSCRIPT_WATCH_CONFIG_PATH, `${JSON.stringify(parsed, null, 2)}\n`);

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Validate the watch config with a JSON linter and fix the syntax error it reports.
  2. Check read/write permissions on the config path.
  3. Close the codex app while the disable step runs so no concurrent write races the rewrite.
  4. If the file is disposable, remove the stale watch entries manually or delete the file if codex regenerates it.

Example fix

// watch config — before (comment makes it unparseable)
{ "watches": [ /* old entries */ ] }
// after (valid JSON the stripper can process)
{ "watches": [] }
Defensive patterns

Strategy: validation

Validate before calling

// validate the watch config before the cleanup pass
const raw = readFileSync(CODEX_TRANSCRIPT_WATCH_CONFIG_PATH, 'utf-8');
JSON.parse(raw); // throws a precise SyntaxError before any stripping logic

Type guard

function isWatchConfig(v: unknown): v is { watches: unknown[] } {
  return typeof v === 'object' && v !== null && Array.isArray((v as { watches?: unknown }).watches);
}

Try / catch

try {
  disableCodexTranscriptAgentsContext();
} catch (e) {
  // false means legacy entries remain: fix the config file, then re-run
  log.warn('legacy transcript watch entries retained', { path: configPath });
}

Prevention

When it happens

Trigger: CODEX_TRANSCRIPT_WATCH_CONFIG_PATH exists but is invalid JSON (hand-edited, truncated, contains comments), unreadable, or unwritable — JSON.parse throws before any filtering runs.

Common situations: Config file edited by hand or truncated by a crash; the codex app writing concurrently during the strip; comments added to JSON; encoding or BOM anomalies.

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@e2d1df569a (2026-08-20). Data as JSON: /api/errors/ba32b7daaa790052. Report an issue: GitHub.