thedotmack/claude-mem · error
Invalid mode ID
Error message
Invalid mode ID: ${modeId} What it means
Before touching the filesystem, loadModeFile validates the mode ID against MODE_ID_PATTERN; an ID that does not match the allowed identifier shape throws 'Invalid mode ID'. This guards against malformed names and path-injection-style inputs (e.g. '../', slashes, illegal characters) before they are joined into a file path.
Solutions
- Use a pattern-compliant mode ID (typically lowercase word characters with single `--` for inheritance).
- Sanitize/normalize user-supplied mode names before passing them to loadMode.
- Check MODE_ID_PATTERN in ModeManager.ts and conform to it exactly.
- Strip path components: pass the bare mode name, not a file path or with extension.
Example fix
// before
modeManager.loadMode(userInput); // '../code'
// after
const id = String(userInput).replace(/\.json$/, '').replace(/[^a-zA-Z0-9-]/g, '');
if (!/^[a-z0-9]+(--[a-z0-9]+)?$/.test(id)) throw new Error('bad mode id');
modeManager.loadMode(id); Defensive patterns
Strategy: type-guard
Validate before calling
if (!/^[A-Za-z0-9]+(--[A-Za-z0-9]+)?$/.test(modeId)) throw new Error(`Mode id '${modeId}' contains characters not allowed by MODE_ID_PATTERN`); Type guard
const isSafeModeId = (id: unknown): id is string =>
typeof id === 'string' && /^[A-Za-z0-9]+(--[A-Za-z0-9]+)?$/.test(id) && !id.includes('/') && !id.includes('..'); Try / catch
try { return manager.loadMode(rawInput); } catch (e) { if (e.message.startsWith('Invalid mode ID')) throw new Error(`'${rawInput}' is not a valid mode id (letters/digits and single -- only)`); throw e; } Prevention
- Never pass raw user input as a mode ID; normalize/whitelist first.
- Strip file extensions and path components before passing names.
- Keep a whitelist of allowed mode ids in client config.
- Check MODE_ID_PATTERN and reuse it wherever mode ids are accepted.
When it happens
Trigger: Calling loadMode()/mode() with an ID containing path separators, dots, spaces, or other characters outside MODE_ID_PATTERN, e.g. `../../etc/passwd`, `my mode`, `Code` if pattern is lowercase-only.
Common situations: Unsanitized user input passed straight to mode loading; a mode ID derived from a file path instead of a bare name; case or hyphen/underscore mismatch with the pattern's expectations.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Adapter rejected input
- agent_event source_id must belong to project_id and team_id
- canonical content
- canonical content
- CLAUDE_MEM_REDIS_URL must use redis:// or rediss://
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/22aa6106d98f2a82.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/domain/ModeManager.ts:87
const result = { ...base } as T;
for (const key in override) {
const overrideValue = override[key];
const baseValue = base[key];
if (this.isPlainObject(overrideValue) && this.isPlainObject(baseValue)) {
result[key] = this.deepMerge(baseValue, overrideValue as any);
} else {
result[key] = overrideValue as T[Extract<keyof T, string>];
}
}
return result;
}
private loadModeFile(modeId: string): ModeConfig {
if (!MODE_ID_PATTERN.test(modeId)) {
throw new Error(`Invalid mode ID: ${modeId}`);
}
const modePath = this.modeDirs
.map(modesDir => join(modesDir, `${modeId}.json`))
.find(candidate => existsSync(candidate));
if (!modePath) {
throw new Error(`Mode file not found: ${modeId}.json (searched: ${this.modeDirs.join(', ')})`);
}
const jsonContent = readFileSync(modePath, 'utf-8');
return JSON.parse(jsonContent) as ModeConfig;
}
loadMode(modeId: string): ModeConfig {
const inheritance = this.parseInheritance(modeId);
if (!inheritance.hasParent) {View on GitHub (pinned to d8bc9755e7)