{"record":{"id":"cffbfc037571172e","repo":"thedotmack/claude-mem","slug":"transcript-file-exists-but-is-empty-transcriptp","errorCode":null,"errorMessage":"Transcript file exists but is empty: ${transcriptPath}","messagePattern":"Transcript file exists but is empty: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/shared/transcript-parser.ts","lineNumber":17,"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\",...}`\n *\n * The most recent assistant turn is often a pure tool_use block with no text\n * content (especially in Cursor, where the agent's last action before the\n * user replies is a tool call). We therefore keep scanning backwards until\n * we find a turn with non-empty text content, instead of returning early on\n * the first matching role.","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/shared/transcript-parser.ts#L1-L35","documentation":"The transcript file exists but reads as empty after trim; extractLastMessage warns and returns ''. This is typically a timing artifact — the JSONL was created but no lines had been flushed when the hook read it — or an externally truncated file.","triggerScenarios":"A hook fires before Claude Code flushes the first JSONL line to the new transcript file, or the file was truncated by disk-full/crash between creation and read.","commonSituations":"Very early hook timing on the first prompt of a session; disk-full leaving zero-byte files; a crashed writer process.","solutions":["Usually transient — the next hook event re-reads a now-populated transcript.","If persistent, open the JSONL directly and confirm Claude Code is writing lines to it.","Check disk space: writers can create empty files when the volume is full."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const content = readFileSync(transcriptPath, 'utf-8').trim();\nif (!content) {\n  // transcript not flushed yet — retry or skip this event\n  return '';\n}","typeGuard":"const hasContent = (s: string): boolean => s.trim().length > 0;","tryCatchPattern":null,"preventionTips":["On the first prompt of a session, tolerate one empty-read and retry on the next hook event.","Monitor disk space — full volumes produce empty transcript files.","If a transcript stays empty across events, check that Claude Code is actually writing the JSONL."],"tags":["transcript","empty-file","parser","claude-mem"],"backgroundTag":"empty-input-file","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}