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
- 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.
- 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.
- 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.
- 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.
- 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
- Never touch localStorage during module evaluation or a useState initializer that can run under SSR; defer reads to useEffect or guard with typeof window !== 'undefined'.
- Wrap every storage access (even reads - .getItem and property access can throw SecurityError) in try/catch with a typed fallback.
- Cache a storage-availability probe at startup and skip all storage calls when it fails, so you log one warning instead of one per access.
- Test the viewer with storage blocked (Chrome: padlock > Site settings > Block cookies) and in jsdom without a storage shim to verify graceful degradation.
- Report the error's name (e.name) alongside the message so SecurityError vs QuotaExceededError vs ReferenceError is distinguishable in logs.
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
- Missing cwd in FileEdit hook input for session ${sessionId},
- bun install failed in ${targetDir} ${describeExecError(err)}
- npmPackageRootDirectory: expected package.json at ${root}. B
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/5310a8dadf58b10a.
Report an issue: GitHub.