{"record":{"id":"22aa6106d98f2a82","repo":"thedotmack/claude-mem","slug":"invalid-mode-id-modeid","errorCode":null,"errorMessage":"Invalid mode ID: ${modeId}","messagePattern":"Invalid mode ID: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/domain/ModeManager.ts","lineNumber":87,"sourceCode":"    const result = { ...base } as T;\n\n    for (const key in override) {\n      const overrideValue = override[key];\n      const baseValue = base[key];\n\n      if (this.isPlainObject(overrideValue) && this.isPlainObject(baseValue)) {\n        result[key] = this.deepMerge(baseValue, overrideValue as any);\n      } else {\n        result[key] = overrideValue as T[Extract<keyof T, string>];\n      }\n    }\n\n    return result;\n  }\n\n  private loadModeFile(modeId: string): ModeConfig {\n    if (!MODE_ID_PATTERN.test(modeId)) {\n      throw new Error(`Invalid mode ID: ${modeId}`);\n    }\n\n    const modePath = this.modeDirs\n      .map(modesDir => join(modesDir, `${modeId}.json`))\n      .find(candidate => existsSync(candidate));\n\n    if (!modePath) {\n      throw new Error(`Mode file not found: ${modeId}.json (searched: ${this.modeDirs.join(', ')})`);\n    }\n\n    const jsonContent = readFileSync(modePath, 'utf-8');\n    return JSON.parse(jsonContent) as ModeConfig;\n  }\n\n  loadMode(modeId: string): ModeConfig {\n    const inheritance = this.parseInheritance(modeId);\n\n    if (!inheritance.hasParent) {","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/domain/ModeManager.ts#L69-L105","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmodeManager.loadMode(userInput); // '../code'\n// after\nconst id = String(userInput).replace(/\\.json$/, '').replace(/[^a-zA-Z0-9-]/g, '');\nif (!/^[a-z0-9]+(--[a-z0-9]+)?$/.test(id)) throw new Error('bad mode id');\nmodeManager.loadMode(id);","handlingStrategy":"type-guard","validationCode":"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`);","typeGuard":"const isSafeModeId = (id: unknown): id is string =>\n  typeof id === 'string' && /^[A-Za-z0-9]+(--[A-Za-z0-9]+)?$/.test(id) && !id.includes('/') && !id.includes('..');","tryCatchPattern":"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; }","preventionTips":["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."],"tags":["mode-manager","validation","path-safety"],"backgroundTag":"invalid-identifier-format","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}