{"record":{"id":"51c38260c6eec7ad","repo":"thedotmack/claude-mem","slug":"invalid-corpus-name","errorCode":"INVALID_CORPUS_NAME","errorMessage":"Invalid corpus name: only alphanumeric characters, dots, hyphens, and underscores are allowed","messagePattern":"Invalid corpus name: only alphanumeric characters, dots, hyphens, and underscores are allowed","errorType":"http","errorClass":"AppError","httpStatus":400,"severity":"warning","filePath":"src/services/worker/knowledge/CorpusStore.ts","lineNumber":100,"sourceCode":"\n    return results;\n  }\n\n  delete(name: string): boolean {\n    const filePath = this.getFilePath(name);\n    if (!fs.existsSync(filePath)) {\n      return false;\n    }\n\n    fs.unlinkSync(filePath);\n    logger.debug('WORKER', `Deleted corpus file: ${filePath}`);\n    return true;\n  }\n\n  private validateCorpusName(name: string): string {\n    const trimmed = name.trim();\n    if (!CORPUS_NAME_PATTERN.test(trimmed)) {\n      throw new AppError(CORPUS_NAME_ERROR, 400, 'INVALID_CORPUS_NAME');\n    }\n    return trimmed;\n  }\n\n  private getFilePath(name: string): string {\n    const safeName = this.validateCorpusName(name);\n    const resolved = path.resolve(this.corporaDir, `${safeName}.corpus.json`);\n    if (!resolved.startsWith(path.resolve(this.corporaDir) + path.sep)) {\n      throw new AppError('Invalid corpus name', 400, 'INVALID_CORPUS_NAME');\n    }\n    return resolved;\n  }\n}\n","sourceCodeStart":82,"sourceCodeEnd":114,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/worker/knowledge/CorpusStore.ts#L82-L114","documentation":"CorpusStore.validateCorpusName rejects corpus names not matching /^[a-zA-Z0-9._-]+$/ (after trim) and throws an AppError with code INVALID_CORPUS_NAME and HTTP 400. This guards the filesystem: the name is used directly in `${safeName}.corpus.json` file paths, so slashes, spaces, and unicode would either break the file layout or enable path traversal.","triggerScenarios":"Creating, reading, or writing a corpus whose name contains characters outside [a-zA-Z0-9._-] — e.g. 'my corpus', 'corp/us', 'café', or an empty/whitespace-only name.","commonSituations":"User-supplied corpus names passed through from a CLI flag or HTTP request; names built by string concatenation from file paths; locale/unicode names pasted from other tools; a name that is only whitespace after trim.","solutions":["Rename the corpus to contain only alphanumerics, dots, hyphens, and underscores","Sanitize user input before passing it to the corpus API (strip or encode disallowed characters)","Return the 400 INVALID_CORPUS_NAME to the caller with the allowed character set so they can correct it","Handle the empty-string case explicitly before calling the store to give a clearer message"],"exampleFix":"// before\nawait store.create(userInputName, content); // 'my corpus' → throws\n// after\nconst safe = userInputName.trim().replace(/[^a-zA-Z0-9._-]/g, '-');\nawait store.create(safe, content);","handlingStrategy":"validation","validationCode":"const CORPUS_NAME_RE = /^[a-zA-Z0-9._-]+$/;\nfunction isValidCorpusName(name: unknown): name is string {\n  return typeof name === 'string' && name.trim().length > 0 && CORPUS_NAME_RE.test(name.trim());\n}","typeGuard":"function isValidCorpusName(name: unknown): name is string {\n  return typeof name === 'string' && /^[a-zA-Z0-9._-]+$/.test(name.trim());\n}","tryCatchPattern":"try {\n  await store.create(name, content);\n} catch (err) {\n  if (err instanceof AppError && err.code === 'INVALID_CORPUS_NAME') {\n    // surface 400 to the user with the allowed character set\n    return res.status(400).json({ error: 'Corpus name may only contain a-z, A-Z, 0-9, dot, hyphen, underscore' });\n  }\n  throw err;\n}","preventionTips":["Sanitize or slugify user-supplied names before passing to the corpus API","Validate names at the CLI/HTTP boundary, not just inside the store","Document the allowed character set where users enter corpus names","Reject empty/whitespace names with a distinct message before calling the store"],"tags":["validation","input-sanitization","filesystem"],"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"}