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

  1. Use a pattern-compliant mode ID (typically lowercase word characters with single `--` for inheritance).
  2. Sanitize/normalize user-supplied mode names before passing them to loadMode.
  3. Check MODE_ID_PATTERN in ModeManager.ts and conform to it exactly.
  4. 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

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


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)