thedotmack/claude-mem · warning
Missing schema for watch
Error message
Missing schema for watch
What it means
When the transcript watcher sets up a watch target, resolveSchema() maps a string watch.schema to an entry in the config's top-level `schemas` map (an inline schema object is used as-is). If the string names a schema that does not exist in that map, the lookup returns null, this warning is logged, and the watch is skipped — no files are tailed for it.
Source
Thrown at src/services/transcripts/watcher.ts:116
await this.setupWatch(watch);
}
}
stop(): void {
for (const tailer of this.tailers.values()) {
tailer.close();
}
this.tailers.clear();
for (const watcher of this.rootWatchers) {
watcher.close();
}
this.rootWatchers = [];
}
private async setupWatch(watch: WatchTarget): Promise<void> {
const schema = this.resolveSchema(watch);
if (!schema) {
logger.warn('TRANSCRIPT', 'Missing schema for watch', { watch: watch.name });
return;
}
const resolvedPath = expandHomePath(watch.path);
const files = this.resolveWatchFiles(resolvedPath);
for (const filePath of files) {
await this.addTailer(filePath, watch, schema);
}
const watchRoot = this.deepestNonGlobAncestor(resolvedPath);
if (!watchRoot || !existsSync(watchRoot)) {
logger.debug('TRANSCRIPT', 'Watch root does not exist, skipping fs.watch', { watch: watch.name, watchRoot });
return;
}
try {
const watcher = fsWatch(watchRoot, { recursive: true, persistent: true }, (event, name) => {View on GitHub (pinned to e2d1df569a)
Solutions
- Open the transcript watch config and check the exact keys under `schemas`; fix the watch's `schema` string to match one of them
- Or replace the string with an inline schema object on the watch itself
- After fixing, restart the worker so setupWatch runs again with the corrected config
Example fix
// before
{ "schemas": { "codex-v1": { ... } }, "watches": [ { "name": "codex", "schema": "codex", "path": "~/.codex/sessions/**/*.jsonl" } ] }
// after
{ "schemas": { "codex-v1": { ... } }, "watches": [ { "name": "codex", "schema": "codex-v1", "path": "~/.codex/sessions/**/*.jsonl" } ] } Defensive patterns
Strategy: validation
Validate before calling
function assertWatchSchemasValid(config: TranscriptWatchConfig): void {
for (const watch of config.watches) {
if (typeof watch.schema === 'string' && !config.schemas?.[watch.schema]) {
throw new Error(`watch '${watch.name}' references unknown schema '${watch.schema}'`);
}
}
} Type guard
function hasResolvableSchema(watch: WatchTarget, schemas?: Record<string, TranscriptSchema>): boolean {
if (typeof watch.schema === 'string') return Boolean(schemas?.[watch.schema]);
return Boolean(watch.schema);
} Prevention
- Keep schema definitions and the watches that reference them in the same config file
- Run a config validator (like the snippet) in CI or before worker start
- After renames, grep the watch config for old schema names
When it happens
Trigger: A watch declares "schema": "codex" (or any name) that has no matching key under `schemas` in the same transcript watch config JSON; the schema was renamed or removed, or the name has a typo/case mismatch.
Common situations: Upgrading claude-mem versions where bundled schema names changed; hand-editing the watch config and misspelling the schema reference; splitting config files so the watch and its schema definition end up in different files.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Skipped Codex transcript watch because native Codex hooks ar
- Transcript watch config not found: ${resolvedPath}
- Invalid transcript watch config: ${resolvedPath}
- ValidationError
- Native Codex hooks registered, but failed to disable legac
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/c25b4ee6c80e2f12.
Report an issue: GitHub.