thedotmack/claude-mem · warning
Transcript file exists but is empty: ${transcriptPath}
Error message
Transcript file exists but is empty: ${transcriptPath} What it means
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.
Source
Thrown at src/shared/transcript-parser.ts:17
import { readFileSync, existsSync } from 'fs';
import { logger } from '../utils/logger.js';
import { SYSTEM_REMINDER_REGEX } from '../utils/tag-stripping.js';
export function extractLastMessage(
transcriptPath: string,
role: 'user' | 'assistant',
stripSystemReminders: boolean = false
): string {
if (!transcriptPath || !existsSync(transcriptPath)) {
logger.warn('PARSER', `Transcript path missing or file does not exist: ${transcriptPath}`);
return '';
}
const content = readFileSync(transcriptPath, 'utf-8').trim();
if (!content) {
logger.warn('PARSER', `Transcript file exists but is empty: ${transcriptPath}`);
return '';
}
return extractLastMessageFromJsonl(content, role, stripSystemReminders);
}
/**
* Extract last message from a JSONL transcript.
*
* Supports two field conventions for the per-line role marker:
* - Claude Code: `{"type":"assistant",...}`
* - Cursor: `{"role":"assistant",...}`
*
* The most recent assistant turn is often a pure tool_use block with no text
* content (especially in Cursor, where the agent's last action before the
* user replies is a tool call). We therefore keep scanning backwards until
* we find a turn with non-empty text content, instead of returning early on
* the first matching role.View on GitHub (pinned to e2d1df569a)
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.
Defensive patterns
Strategy: validation
Validate before calling
const content = readFileSync(transcriptPath, 'utf-8').trim();
if (!content) {
// transcript not flushed yet — retry or skip this event
return '';
} Type guard
const hasContent = (s: string): boolean => s.trim().length > 0;
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Very early hook timing on the first prompt of a session; disk-full leaving zero-byte files; a crashed writer process.
Related errors
- Transcript path missing or file does not exist: ${transcript
- Generator paused for ${abortCategory}; preserving buffered w
- Invalid mode inheritance: ${modeId}. Only one level of inher
- Invalid mode ID: ${modeId}
- Mode file not found: ${modeId}.json (searched: ${this.modeDi
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/cffbfc037571172e.
Report an issue: GitHub.