thedotmack/claude-mem · warning
Could not restrict permissions on ${path} to 0600: ${chmodEr
Error message
Could not restrict permissions on ${path} to 0600: ${chmodError instanceof Error ? chmodError.message : String(chmodError)} What it means
After atomically writing settings.json, the installer tries chmod 0o600 because the file can contain tokens (CMEM Pro setup token, provider API keys) and a umask-default 0644 file would be world-readable. This warning means chmodSync failed — the settings write itself succeeded, but the file may be readable by other users. Deliberately fail-soft, not silent.
Source
Thrown at src/npx-cli/commands/install.ts:824
}
}
const target = envNested
? (document.env as Record<string, unknown>)
: document;
for (const [key, value] of Object.entries(updates)) {
target[key] = value;
}
writeSettingsJsonAtomic(path, document);
// settings.json can carry tokens (CMEM Pro setup token, provider API
// keys); a fresh file inherits the umask (usually 0644), leaving them
// world-readable. Tighten to owner-only. Fail-soft: a chmod failure must
// never fail the settings write itself, but it is not silent.
try {
chmodSync(path, 0o600);
} catch (chmodError: unknown) {
log.warn(`Could not restrict permissions on ${path} to 0600: ${chmodError instanceof Error ? chmodError.message : String(chmodError)}`);
}
return true;
} catch (error: unknown) {
log.error(`Failed to write settings to ${path}: ${error instanceof Error ? error.message : String(error)}`);
return false;
}
}
type ProviderId = 'claude' | 'gemini' | 'openrouter';
/**
* What the installer prompt may offer. `cmem` is a prompt-only sentinel: picking
* it configures the generic OpenAI-compatible path (base URL + model + key) and
* persists CLAUDE_MEM_PROVIDER='openrouter'. The worker only understands
* 'claude' | 'gemini' | 'openrouter', so 'cmem' must never reach settings.json.
*/
type ProviderChoice = ProviderId | 'cmem';
type ClaudeAccessMode = 'subscription' | 'api-key';
type ClaudeApiMode = 'direct' | 'gateway';View on GitHub (pinned to e2d1df569a)
Solutions
- Fix manually: `chmod 600 ~/.claude-mem/settings.json` (on POSIX) and verify with `stat -c %a`.
- On Windows/network filesystems, rely on ACLs instead: restrict the folder so only your user can read it.
- If secrets were written while the file was group/world-readable, rotate them (API keys, `npx claude-mem server keys rotate`) — the warning means exposure was possible.
- Move ~/.claude-mem onto a POSIX filesystem if the host mount permanently ignores modes.
Example fix
# after seeing this warning, tighten manually and verify: chmod 600 ~/.claude-mem/settings.json stat -c '%a %n' ~/.claude-mem/settings.json # expect: 600
Defensive patterns
Strategy: try-catch
Validate before calling
// Verify post-conditions after install instead of trusting the chmod:
import { statSync } from 'node:fs';
const mode = statSync(`${process.env.HOME}/.claude-mem/settings.json`).mode & 0o777;
if (mode !== 0o600) {
// on POSIX tighten manually; on Windows/ACL fs rely on folder ACLs
} Try / catch
// The correct pattern is exactly what the installer does — fail-soft but loud:
try { chmodSync(path, 0o600); }
catch (e) { log.warn(`permissions not restricted: ${e instanceof Error ? e.message : e}`); } Prevention
- Keep ~/.claude-mem on a POSIX filesystem (not FAT/exFAT/SMB) if you store tokens there.
- After any warning, rotate secrets that may have been world-readable, then chmod 600.
- On Windows, restrict the parent folder's ACL to your user as the equivalent control.
- Include a mode check in provisioning scripts: fail the pipeline when mode != 600.
When it happens
Trigger: chmodSync throwing: filesystems that do not support POSIX modes (Windows FAT, some network mounts, exFAT), the file deleted/moved by another process between write and chmod, or running without ownership of the file.
Common situations: HOME on a Windows drive or SMB/NFS mount without Unix permissions; container with a volume that ignores mode bits; another agent rotating the settings file concurrently.
Related errors
- claude-mem: could not read ${USER_SETTINGS_PATH} while check
- [install] Failed to parse existing settings.json, starting f
- installPluginDependencies: no package.json at ${targetDir}
- generation parse error: ${outcome.reason}
- generation job ${job.id} not found in scope
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/cba7bf37f17269b6.
Report an issue: GitHub.