thedotmack/claude-mem · warning
Missing cwd in FileEdit hook input for session ${sessionId},
Error message
Missing cwd in FileEdit hook input for session ${sessionId}, file ${filePath} What it means
This is a console.warn from claude-mem's catch block around localStorage.setItem/removeItem (src/ui/viewer/components/WelcomeCard.tsx:20-30). Web Storage writes throw in two main cases: QuotaExceededError when the origin's ~5MB localStorage budget is exhausted (or is 0, as in old Safari private browsing where setItem always threw), and SecurityError when the browser has blocked site data for the origin. Because only the string 'true' is written, quota exhaustion is almost never this key's fault - the origin is already full, or writes are categorically blocked.
Source
Thrown at src/cli/handlers/file-edit.ts:23
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',
{
contentSessionId: sessionId,
platformSource,
tool_name: 'write_file',
tool_input: { filePath, edits },
tool_response: { success: true },
cwd,
},View on GitHub (pinned to e2d1df569a)
Solutions
- Check DevTools > Application > Local Storage: if the origin shows ~5MB used, clear stale keys (or export and purge old claude-mem caches) so this one-key write can succeed again.
- Verify site data is allowed for the origin (padlock icon > Site settings > Cookies and site data); blocked storage produces SecurityError on every write regardless of quota.
- Add a quota-aware write: catch the error, inspect e.name === 'QuotaExceededError', evict the largest/oldest non-essential keys under your control, then retry setItem once.
- If persistence is best-effort (it is - a boolean preference), mirror the flag to sessionStorage or an in-memory fallback so dismissal survives the session even when localStorage is unwritable.
- For embedded/sandboxed contexts, request allow-same-origin plus storage access from the embedding host, or accept the re-show behavior.
Example fix
// before - fire-and-forget write, quota/security failures only logged
localStorage.setItem(STORAGE_KEY, 'true');
// after - classify the failure, evict and retry once on quota, keep an in-memory fallback
try {
localStorage.setItem(STORAGE_KEY, 'true');
} catch (e) {
if (e instanceof DOMException && e.name === 'QuotaExceededError') {
evictStaleKeys(); // remove old non-essential keys for this origin
try { localStorage.setItem(STORAGE_KEY, 'true'); return; } catch { /* fall through */ }
}
memoryFallback[STORAGE_KEY] = 'true'; // dismissal still works for this session
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before writing, confirm writable storage and remaining headroom
function canWriteStorage(): boolean {
try {
const k = '__claude-mem-probe__';
window.localStorage.setItem(k, '1');
window.localStorage.removeItem(k);
return true;
} catch {
return false;
}
}
if (canWriteStorage()) {
setStoredWelcomeDismissed(true);
} else {
sessionStorage.setItem(STORAGE_KEY, 'true'); // session-scoped fallback
} Type guard
function isQuotaExceeded(e: unknown): e is DOMException & { name: 'QuotaExceededError' } {
return e instanceof DOMException &&
(e.name === 'QuotaExceededError' || e.code === 22);
}
function isStorageDenied(e: unknown): e is DOMException & { name: 'SecurityError' } {
return e instanceof DOMException && e.name === 'SecurityError';
} Try / catch
// Write-specific pattern: classify, retry once on quota, otherwise degrade silently
try {
localStorage.setItem(STORAGE_KEY, 'true');
} catch (e: unknown) {
if (isQuotaExceeded(e)) {
evictStaleKeysForOrigin();
try { localStorage.setItem(STORAGE_KEY, 'true'); } catch { /* give up quietly */ }
} else {
console.warn('welcome-dismissed not persisted (storage blocked):', e instanceof Error ? e.message : String(e));
}
} Prevention
- Treat storage writes as best-effort: keep the payload tiny (this key stores just 'true') and never let a failed write break the dismiss flow in the UI.
- Prune or version your storage keys (the v3 suffix on STORAGE_KEY already does this) and evict data from obsolete versions so quota creep does not doom future writes.
- On quota failure, retry once after evicting your own stale keys; do not loop retries against a hard-blocked origin.
- Keep a sessionStorage or in-memory mirror for UI preferences so a blocked localStorage only costs persistence, not correctness.
- Include e.name (QuotaExceededError/SecurityError) in logs and cap the warning to once per session to avoid console spam.
When it happens
Trigger: setStoredWelcomeDismissed(true) is called when the user dismisses the welcome modal (WelcomeCard.tsx:164), and setStoredWelcomeDismissed(false) on reset (App.tsx:106). The write throws when: the origin's localStorage is already at the 5MB cap (large saved sessions/observations under other keys); the user is in old Safari Private Browsing (historically quota 0); site data is blocked (SecurityError); or the context is a partitioned/sandboxed iframe where writes are denied.
Common situations: A long-lived viewer origin bloated with cached data that finally crosses the quota; users in private/incognito sessions on older Safari; Chrome's 'Block all cookies' or per-site data blocking; the viewer embedded in a third-party iframe after storage partitioning; jsdom tests writing without a storage implementation. Symptom: dismissal is forgotten and the welcome card reappears every session.
Related errors
- fileEditHandler requires filePath
- npmPackageRootDirectory: expected package.json at ${root}. B
- bun install failed in ${targetDir} ${describeExecError(err)}
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/12f5d41337280215.
Report an issue: GitHub.