thedotmack/claude-mem · warning

fileEditHandler requires filePath

Error message

fileEditHandler requires filePath

What it means

This is a console.warn emitted by claude-mem's own catch block around localStorage.getItem (src/ui/viewer/components/WelcomeCard.tsx:11-18). The Web Storage API can throw at access time: SecurityError when the browser denies storage for the origin (site data/cookies blocked), ReferenceError when the localStorage global does not exist (SSR, Node, jsdom without a storage shim), and opaque-origin errors on sandboxed iframes or about:blank-style contexts. The wrapper is doing its job: it logs the reason and falls back to false so the welcome card still renders.

Source

Thrown at src/cli/handlers/file-edit.ts:15

import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
import { executeWithWorkerFallback, isWorkerFallback } from '../../shared/worker-utils.js';
import { logger } from '../../utils/logger.js';
import { HOOK_EXIT_CODES } from '../../shared/hook-constants.js';
import { normalizePlatformSource } from '../../shared/platform-source.js';
import { shouldTrackProject } from '../../shared/should-track-project.js';

export const fileEditHandler: EventHandler = {
  async execute(input: NormalizedHookInput): Promise<HookResult> {
    const { sessionId, cwd, filePath, edits } = input;
    const platformSource = normalizePlatformSource(input.platform);

    if (!filePath) {
      throw new Error('fileEditHandler requires filePath');
    }

    logger.dataIn('HOOK', `FileEdit: ${filePath}`, {
      editCount: edits?.length ?? 0
    });

    if (!cwd) {
      throw new Error(`Missing cwd in FileEdit hook input for session ${sessionId}, file ${filePath}`);
    }

    if (!shouldTrackProject(cwd)) {
      logger.debug('HOOK', 'Project excluded from tracking, skipping file edit observation', { cwd, filePath });
      return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
    }

    const result = await executeWithWorkerFallback<{ status?: string }>(
      '/api/sessions/observations',
      'POST',

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Confirm it is environmental: open DevTools > Application > Local Storage/Session Storage for the page; if the pane errors or the origin is listed as blocked, allow site data for that origin in browser settings and reload.
  2. If the viewer can ever be server-rendered or prerendered, move the read out of the useState initializer into a useEffect, or gate it with typeof window !== 'undefined', because App.tsx:18 executes the read during the first render where localStorage may not exist.
  3. Add a one-time storage-availability probe (try window.localStorage.setItem('__t','1') then removeItem) cached in a module variable, and have getStoredWelcomeDismissed consult it before touching localStorage.
  4. If none apply and the warning appears in an embedded webview/iframe, ask the host to add allow-same-origin (and storage permission) to the sandbox attribute.
  5. Accept the fallback: the function already returns false and the UI degrades to showing the welcome card; if that is acceptable, downgrading this log to console.debug in embedded contexts removes the noise.

Example fix

// before (src/ui/viewer/App.tsx:18) - runs during first render, breaks under SSR/blocked storage
const [welcomeDismissed, setWelcomeDismissed] = useState<boolean>(getStoredWelcomeDismissed);

// after - read storage only in the browser, after mount
const [welcomeDismissed, setWelcomeDismissed] = useState<boolean>(false);
useEffect(() => {
  setWelcomeDismissed(getStoredWelcomeDismissed());
}, []);
Defensive patterns

Strategy: try-catch

Validate before calling

// Probe storage availability once, before the first render reads it
let storageOk: boolean | null = null;
function storageAvailable(): boolean {
  if (storageOk !== null) return storageOk;
  try {
    const k = '__claude-mem-probe__';
    window.localStorage.setItem(k, '1');
    window.localStorage.removeItem(k);
    storageOk = true;
  } catch {
    storageOk = false;
  }
  return storageOk;
}

// in src/ui/viewer/App.tsx - avoid the read entirely when there is no window/storage
const [welcomeDismissed, setWelcomeDismissed] = useState<boolean>(
  () => typeof window !== 'undefined' && storageAvailable() && getStoredWelcomeDismissed()
);

Type guard

function hasLocalStorage(): boolean {
  try {
    return typeof window !== 'undefined' && window.localStorage !== null;
  } catch {
    return false; // accessing .localStorage itself can throw SecurityError
  }
}

Try / catch

// Keep the existing shape: catch unknown, narrow to Error for the message,
// log at warn/debug exactly once, and return a safe default. Never rethrow from a read path.
try {
  return localStorage.getItem(STORAGE_KEY) === 'true';
} catch (e: unknown) {
  console.warn('Failed to read welcome-dismissed from localStorage:', e instanceof Error ? e.message : String(e));
  return false;
}

Prevention

When it happens

Trigger: Specifically triggered when getStoredWelcomeDismissed() runs (it is the lazy initializer at src/ui/viewer/App.tsx:18, so it executes during the very first render) and any of these holds: Chrome set to 'Block all cookies' or the site's data blocked via the padlock icon; the viewer is embedded in a sandboxed iframe without allow-same-origin; the page is served from an opaque or file:// origin; the component renders outside a real browser (SSR/prerender/test runner) so localStorage is undefined; an enterprise policy or extension disables Web Storage.

Common situations: Lockdown privacy settings (Chrome 'Block third-party cookies' + site-data blocking, Firefox 'Never Save History' mode which disables storage), Safari content blockers, corporate-managed browsers with storage disabled, running the viewer UI inside a restricted webview/iframe (e.g. an IDE extension webview), and vitest/jsdom tests that execute App.tsx without configuring a localStorage. Also seen after browsers rolled out storage partitioning for embedded contexts.

Related errors


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