{"record":{"id":"4da36481e7e58c18","repo":"thedotmack/claude-mem","slug":"transcript-path-missing-or-file-does-not-exist","errorCode":null,"errorMessage":"Transcript path missing or file does not exist: ${transcriptPath}","messagePattern":"Transcript path missing or file does not exist: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/shared/transcript-parser.ts","lineNumber":11,"sourceCode":"import { readFileSync, existsSync } from 'fs';\nimport { logger } from '../utils/logger.js';\nimport { SYSTEM_REMINDER_REGEX } from '../utils/tag-stripping.js';\n\nexport function extractLastMessage(\n  transcriptPath: string,\n  role: 'user' | 'assistant',\n  stripSystemReminders: boolean = false\n): string {\n  if (!transcriptPath || !existsSync(transcriptPath)) {\n    logger.warn('PARSER', `Transcript path missing or file does not exist: ${transcriptPath}`);\n    return '';\n  }\n\n  const content = readFileSync(transcriptPath, 'utf-8').trim();\n  if (!content) {\n    logger.warn('PARSER', `Transcript file exists but is empty: ${transcriptPath}`);\n    return '';\n  }\n\n  return extractLastMessageFromJsonl(content, role, stripSystemReminders);\n}\n\n/**\n * Extract last message from a JSONL transcript.\n *\n * Supports two field conventions for the per-line role marker:\n * - Claude Code:  `{\"type\":\"assistant\",...}`\n * - Cursor:       `{\"role\":\"assistant\",...}`","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/shared/transcript-parser.ts#L1-L29","documentation":"extractLastMessage validates the transcript path first: if it is empty/falsy or existsSync reports false, it warns and returns '' instead of throwing. Callers receive an empty string, which downstream typically means 'nothing to ingest' for that hook event.","triggerScenarios":"A hook calls extractLastMessage(transcriptPath, role) with an empty string, or the JSONL transcript under ~/.claude/projects was deleted/moved between session start and the hook read.","commonSituations":"Session resume referencing a pruned transcript; transcript-cleanup utilities running concurrently; hook config passing a malformed or wrong-machine path.","solutions":["Inspect the hook's JSON input to verify the transcript_path actually passed in.","If the file was rotated or deleted, start a fresh session so Claude Code writes a new transcript.","Disable or reschedule any cleanup that deletes ~/.claude/projects transcripts while claude-mem runs."],"exampleFix":"// before\nconst content = readFileSync(transcriptPath, 'utf-8'); // throws ENOENT\n\n// after\nif (!transcriptPath || !existsSync(transcriptPath)) {\n  logger.warn('PARSER', `Transcript path missing or file does not exist: ${transcriptPath}`);\n  return '';\n}","handlingStrategy":"validation","validationCode":"if (!transcriptPath || !existsSync(transcriptPath)) {\n  // skip this hook event rather than parsing a missing file\n  return '';\n}","typeGuard":"import { existsSync } from 'fs';\nconst isReadableTranscript = (p?: string | null): p is string =>\n  typeof p === 'string' && p.length > 0 && existsSync(p);","tryCatchPattern":null,"preventionTips":["Validate transcript_path from hook JSON input before invoking the parser.","Don't run transcript-cleanup jobs against ~/.claude/projects while sessions are active.","Treat empty-string returns as 'skip event', not as data."],"tags":["transcript","file-not-found","parser","claude-mem"],"backgroundTag":"file-not-found","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}