nexu-io/open-design · error · EmptyTranscriptError
EMPTY_TRANSCRIPT
EMPTY_TRANSCRIPT
Error message
conversation ${options.conversationId} has no messages to hand off What it means
EmptyTranscriptError: exportProjectTranscript returned messageCount === 0 for the given conversationId. synthesizeHandoffPrompt refuses to spend BYOK tokens fabricating a resume prompt from an empty conversation. The route maps this to 400 EMPTY_TRANSCRIPT.
Source
Thrown at apps/daemon/src/design/handoff-design.ts:180
): Promise<HandoffResponse> {
const project = getProject(db, projectId);
if (!project) {
throw new Error(`project not found: ${projectId}`);
}
const now = options.now ?? (() => new Date());
const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
const transcriptResult = exportProjectTranscript(db, projectsRoot, projectId, {
now,
conversationId: options.conversationId,
});
// Fail fast on an empty conversation: synthesizing a handoff from zero
// messages would spend BYOK tokens to fabricate context that does not
// exist. The route maps EmptyTranscriptError to 400.
if (transcriptResult.messageCount === 0) {
throw new EmptyTranscriptError(
`conversation ${options.conversationId} has no messages to hand off`,
);
}
const transcriptJsonl = fs.readFileSync(transcriptResult.path, 'utf8');
const truncatedJsonl = truncateTranscriptForPrompt(transcriptJsonl);
const { systemPrompt, userPrompt } = buildHandoffPrompt({
projectId,
transcriptJsonl: truncatedJsonl,
transcriptMessageCount: transcriptResult.messageCount,
now: now(),
});
const timeoutController = new AbortController();
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
// The timeout must stay armed until the response BODY has been fully
// read, not just until headers arrive. `fetch()` resolves as soon as the
// upstream sends headers, so clearing the timeout before `response.json()`View on GitHub (pinned to 5be4028344)
Solutions
- Send at least one message in the conversation before requesting a handoff.
- Verify the conversationId is correct and belongs to the project.
- Check the conversation has messages via the chat history API before calling handoff.
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the conversation has messages before requesting a handoff.
import { exportProjectTranscript } from '../transcript-export.js';
const probe = exportProjectTranscript(db, projectsRoot, projectId,
{ now: () => new Date(), conversationId });
if (probe.messageCount === 0) {
return res.status(400).json({ code: 'EMPTY_TRANSCRIPT', error: 'send a message first' });
} Type guard
import { EmptyTranscriptError } from './handoff-design.js';
function isEmptyTranscript(err: unknown): err is EmptyTranscriptError {
return err instanceof EmptyTranscriptError;
} Try / catch
import { EmptyTranscriptError } from './handoff-design.js';
try {
const handoff = await synthesizeHandoffPrompt(db, projectsRoot, projectId, options);
} catch (err) {
if (err instanceof EmptyTranscriptError) {
// Route maps to 400 EMPTY_TRANSCRIPT; prompt the user to send a message first.
return res.status(400).json({ code: 'EMPTY_TRANSCRIPT', error: err.message });
}
throw err;
} Prevention
- Disable the handoff UI action until the conversation has at least one message.
- Verify the conversationId belongs to the project before calling.
- Fetch the conversation's message count via the chat API as a pre-check.
When it happens
Trigger: Calling synthesizeHandoffPrompt (POST /api/projects/:id/handoff) with a conversationId that has zero messages — a freshly created conversation, a conversation whose messages were all deleted, or a wrong/mismatched conversationId.
Common situations: User clicked 'handoff' on a brand-new conversation before sending any message; conversationId doesn't match any real conversation; messages were cleared; the conversation belongs to a different project.
Related errors
- Cannot parse line: {line}
- Unsupported message line shape: {line}
- invalid JSON in ${filePath}: ${message}
- ${filePath} must contain a JSON object
- ARTIFACT_MANIFEST_INVALID
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/d47aaf9a1ec1d53b.
Report an issue: GitHub.