thedotmack/claude-mem · warning · AppError

INVALID_CORPUS_NAME

INVALID_CORPUS_NAME

Error message

Invalid corpus name: only alphanumeric characters, dots, hyphens, and underscores are allowed

What it means

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.

Solutions

  1. Rename the corpus to contain only alphanumerics, dots, hyphens, and underscores
  2. Sanitize user input before passing it to the corpus API (strip or encode disallowed characters)
  3. Return the 400 INVALID_CORPUS_NAME to the caller with the allowed character set so they can correct it
  4. Handle the empty-string case explicitly before calling the store to give a clearer message

Example fix

// before
await store.create(userInputName, content); // 'my corpus' → throws
// after
const safe = userInputName.trim().replace(/[^a-zA-Z0-9._-]/g, '-');
await store.create(safe, content);
Defensive patterns

Strategy: validation

Validate before calling

const CORPUS_NAME_RE = /^[a-zA-Z0-9._-]+$/;
function isValidCorpusName(name: unknown): name is string {
  return typeof name === 'string' && name.trim().length > 0 && CORPUS_NAME_RE.test(name.trim());
}

Type guard

function isValidCorpusName(name: unknown): name is string {
  return typeof name === 'string' && /^[a-zA-Z0-9._-]+$/.test(name.trim());
}

Try / catch

try {
  await store.create(name, content);
} catch (err) {
  if (err instanceof AppError && err.code === 'INVALID_CORPUS_NAME') {
    // surface 400 to the user with the allowed character set
    return res.status(400).json({ error: 'Corpus name may only contain a-z, A-Z, 0-9, dot, hyphen, underscore' });
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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/51c38260c6eec7ad. Report an issue: GitHub.

Appendix: source

Thrown at src/services/worker/knowledge/CorpusStore.ts:100

    return results;
  }

  delete(name: string): boolean {
    const filePath = this.getFilePath(name);
    if (!fs.existsSync(filePath)) {
      return false;
    }

    fs.unlinkSync(filePath);
    logger.debug('WORKER', `Deleted corpus file: ${filePath}`);
    return true;
  }

  private validateCorpusName(name: string): string {
    const trimmed = name.trim();
    if (!CORPUS_NAME_PATTERN.test(trimmed)) {
      throw new AppError(CORPUS_NAME_ERROR, 400, 'INVALID_CORPUS_NAME');
    }
    return trimmed;
  }

  private getFilePath(name: string): string {
    const safeName = this.validateCorpusName(name);
    const resolved = path.resolve(this.corporaDir, `${safeName}.corpus.json`);
    if (!resolved.startsWith(path.resolve(this.corporaDir) + path.sep)) {
      throw new AppError('Invalid corpus name', 400, 'INVALID_CORPUS_NAME');
    }
    return resolved;
  }
}

View on GitHub (pinned to d8bc9755e7)