[BUG] /export produces 0-byte files and empty clipboard — rendering pipeline returns empty string

Resolved 💬 13 comments Opened Apr 10, 2026 by ylluminarious Closed Apr 19, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

/export consistently produces empty output. Both the file and clipboard paths generate 0 bytes of content. The command appears to succeed — the "Conversation exported to: ..." message is displayed — but the resulting file is 0 bytes. Clipboard export similarly reports success while the clipboard is empty. This occurs regardless of conversation length, including long sessions with hundreds of messages.

What Should Happen?

/export test-output should produce a non-empty file containing the conversation transcript. /export → "Copy to clipboard" should place the conversation text on the system clipboard.

Error Messages/Logs

No error is displayed. The success message shows normally:
  "Conversation exported to: test-output.txt"
But:
  $ ls -la test-output.txt
  -rw-r--r--  1 user  staff  0 Apr  9 19:30 test-output.txt

Steps to Reproduce

  1. Start a Claude Code session (CLI, Homebrew cask binary)
  2. Have any conversation (even a single exchange works)
  3. Run /export test-output
  4. Check file size: ls -la test-output.txt → 0 bytes
  5. Or: run /export, select "Copy to clipboard", paste → empty

Claude Model

Opus

Is this a regression?

Yes; /export worked in previous versions.

Claude Code Version

2.1.98 (Claude Code)

Platform

Anthropic API

Operating System

Multiple: macOS, also confirmed on Fedora Linux by @Cianidos

Terminal/Shell

iTerm2

Additional Information

I traced through the minified source in the npm package (cli.js from @anthropic-ai/claude-code@2.1.98). The rendering pipeline for /export is:

/export → agY() → ogY() → zi8() → igY() → c58()

Root cause: c58 (render-to-string) returns empty, and igY has no recovery path.

c58 renders the transcript via Ink to a PassThrough stream. It captures only the first data event:

async function c58(q, K) {
    let _ = "", z = false, Y = new PassThrough();
    if (K !== undefined) Y.columns = K;
    return Y.on("data", (O) => {
        if (z) return;
        z = true;
        _ = O.toString();
    }),
    await (await XB(createElement(FkY, null, q), 
           { stdout: Y, patchConsole: false })).waitUntilExit(),
    _;
}

The PassThrough has no columns/rows/isTTY properties (the Ink renderer defaults to 80×24, so dimensions aren't the direct cause). The FkY wrapper exits via setTimeout(exit, 0) after one render frame.

Then igY breaks on the first empty chunk with no fallback:

for (let j = 0; j < $; j += A) {
    let H = await w([j, j + A]);
    if (MO(H).trim() === "") break;  // all-or-nothing exit
    await _(H);
}

MO is stripAnsi. If the rendered frame is whitespace/ANSI-only, trim() yields "" and the entire loop exits immediately — zero content is collected.

Both export paths then silently write the empty string:

  • File: _i8(filename, "") → 0-byte file + success message
  • Clipboard: WP("")pbcopy with empty stdin + success message

Three possible triggers (couldn't narrow further without debugger access):

A. Stream timing — The Homebrew cask binary runs on Bun (Bun.FFI / Bun.stripANSI confirmed in binary strings). Bun's PassThrough may not synchronously emit the data event on .write(). If it fires after waitUntilExit() resolves, _ stays "".

B. Empty screen renderrenderFullFrame (the non-TTY path, since PassThrough.isTTY is undefined) iterates screen cells. If Yoga layout produces zero-height content, the output is only newlines → stripAnsi().trim()"".

C. Render contextc58 creates a fresh Ink instance with a bare AppStateProvider (no initialState). If the transcript component reads any state from context rather than its direct props, it sees an empty default state.

Suggested fixes:

  1. Collect all data chunks instead of only the first event
  2. Use continue instead of break in igY, or remove the early exit
  3. Report empty export as a failure — don't show a success message for 0-byte output

View original on GitHub ↗

This issue has 13 comments on GitHub. Read the full discussion on GitHub ↗