thedotmack/claude-mem · warning

Telemetry: corrupt telemetry.json; treating as no recorded c

Error message

Telemetry: corrupt telemetry.json; treating as no recorded consent

What it means

loadTelemetryConfig() wraps readJsonSafe; malformed or missing JSON is already handled inside (returning null without throwing), so this catch covers IO exceptions such as EACCES/EPERM/EIO. It returns null meaning 'no recorded consent', so the default consent policy applies until a decision is (re)written. The function never throws by contract.

Source

Thrown at src/services/telemetry/consent.ts:108

}

/** Absolute path of telemetry.json inside the claude-mem data dir. */
export function getTelemetryConfigPath(): string {
  return join(resolveDataDir(), TELEMETRY_CONFIG_FILENAME);
}

/**
 * Reads telemetry.json from the data dir. Returns null if the file is
 * missing, corrupt, or malformed — never throws.
 */
export function loadTelemetryConfig(): TelemetryConfig | null {
  let raw: Partial<TelemetryConfig> | null;
  try {
    raw = readJsonSafe<Partial<TelemetryConfig> | null>(getTelemetryConfigPath(), null);
  } catch (error) {
    // Corrupt JSON — treat as no recorded consent
    const err = error instanceof Error ? error : new Error(String(error));
    logger.warn('SYSTEM', 'Telemetry: corrupt telemetry.json; treating as no recorded consent', undefined, err);
    return null;
  }
  if (!raw || typeof raw !== 'object') return null;
  if (typeof raw.installId !== 'string') return null;
  // enabled may be absent (no decision recorded — default applies), but a
  // present non-boolean value means the file is malformed.
  if (raw.enabled !== undefined && typeof raw.enabled !== 'boolean') return null;
  return {
    enabled: raw.enabled,
    installId: raw.installId,
    decidedAt: typeof raw.decidedAt === 'string' ? raw.decidedAt : '',
  };
}

export function saveTelemetryConfig(config: TelemetryConfig): void {
  const dataDir = resolveDataDir();
  mkdirSync(dataDir, { recursive: true });
  writeFileSync(join(dataDir, TELEMETRY_CONFIG_FILENAME), JSON.stringify(config, null, 2) + '\n');

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Restore read permission on telemetry.json in the data dir
  2. If the file was hand-edited into invalid JSON, readJsonSafe already treats it as no-consent — fix or delete it
  3. Re-record the consent decision in-app afterwards so future loads are clean

Example fix

// before
const cfg = loadTelemetryConfig();
if (cfg?.enabled === true) startTelemetry();

// after — treat null as 'no decision recorded' and persist a fresh one
const cfg = loadTelemetryConfig();
if (cfg === null) {
  const enabled = await promptForConsent();
  persistTelemetryConfig({
    enabled,
    installId: ensureInstallId(),
    decidedAt: new Date().toISOString(),
  });
} else if (cfg.enabled === true) {
  startTelemetry();
}
Defensive patterns

Strategy: fallback

Validate before calling

import { readFileSync } from 'node:fs';

function telemetryConfigReadable(path: string): boolean {
  try {
    JSON.parse(readFileSync(path, 'utf8'));
    return true;
  } catch {
    return false;
  }
}

Type guard

function isTelemetryConfig(v: unknown): v is { enabled?: boolean; installId: string; decidedAt?: string } {
  if (typeof v !== 'object' || v === null) return false;
  const c = v as Record<string, unknown>;
  if (typeof c.installId !== 'string') return false;
  if (c.enabled !== undefined && typeof c.enabled !== 'boolean') return false;
  return true;
}

Prevention

When it happens

Trigger: telemetry.json exists but cannot be read due to filesystem permission errors or the file being locked by other software — distinct from invalid JSON, which is handled silently.

Common situations: File permissions changed after install; backup or AV software locking the file; the data dir migrated between users or restored from an archive.

Related errors


AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20). Data as JSON: /api/errors/621ae49bc1334bc5. Report an issue: GitHub.