MoonshotAI / MoonshotAI/kimi-cli

Bug: kimi export crashes during context compaction — API 400 text content is empty

Open Beginner friendly
#2,396 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
11.4k
Forks
1.3k
Avg merge
9h 47m
Merged PRs (30d)
2

Description

Summary

Typing kimi export can fail with a Moonshot API 400 error when the session enters context compaction. The compaction message contains empty/whitespace-only TextParts, which the API rejects.

Environment

  • kimi-cli version: 1.45.0
  • Python version: 3.12.13
  • OS: Linux 6.18.27-amd64-desktop-rolling (x86_64)
  • Model: kimi-code/kimi-for-coding

Steps to Reproduce

  1. Start a session that accumulates enough context to trigger auto-compaction.
  2. Ensure some messages in history have empty or whitespace-only TextParts (e.g., tool results with no text output).
  3. Run kimi export.
  4. The shell begins compaction (CompactionBegin) and immediately crashes.

Actual Behavior

2026-05-28 21:11:28.599 | CompactionBegin
2026-05-28 21:11:30.254 | StepInterrupted
2026-05-28 21:11:30.254 | ERROR | Context compaction failed at step 1: APIStatusError: Error code: 400 - text content is empty

Expected Behavior

kimi export should complete successfully even when the context contains messages with empty text content.

Root Cause

In kimi_cli/soul/compaction.py, SimpleCompaction.prepare() builds a compaction prompt by iterating over historical messages and appending their TextParts:

compact_message.content.extend(
    part for part in msg.content if isinstance(part, TextPart)
)

This does not filter out TextParts where text == "" or text.strip() == "". When the resulting compaction message is sent to kosong.step(), the Moonshot API rejects it with:
{"error": {"message": "text content is empty", "type": "invalid_request_error"}}

A similar bug for tool messages was previously fixed (see #1663 in message.py), but the compaction path was missed.

Proposed Fix

Two defensive changes in kimi_cli/soul/compaction.py, method SimpleCompaction.prepare():

1. Filter empty text parts
compact_message.content.extend(
    part for part in msg.content
    if isinstance(part, TextPart) and part.text.strip()
)
2. Skip compaction if nothing meaningful remains
compact_message.content.append(TextPart(text=prompt_text))
if not any(
    isinstance(p, TextPart) and p.text.strip()
    for p in compact_message.content
):
    return self.PrepareResult(compact_message=None, to_preserve=messages)
return self.PrepareResult(compact_message=compact_message, to_preserve=to_preserve)

Attachments

Sanitized session export (PII redacted): session-df52da1f-sanitized.zip

Related PR

  • #2395

Additional Context

The crash occurs at the kosong.step() call inside SimpleCompaction.compact(), not during serialization of the final compacted result. Therefore the fix must be applied before the LLM call, not after.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in kimi_cli/soul/compaction.py at SimpleCompaction.prepare(), then trace the compaction call from SimpleCompaction.compact() to understand where the message is sent. Verify that empty or whitespace-only TextParts are excluded and that export completes, or skips compaction when no meaningful text remains.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.