MoonshotAI / MoonshotAI/kimi-cli

bug: Tool call arguments double-encoding breaks array/dict parameters (Moonshot API)

Open Beginner friendly
#2,406 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

Problem

Tool calls with array or object parameters (e.g. SetTodoList, ExitPlanMode, StrReplaceFile) fail with Pydantic validation errors:

Error validating JSON arguments: 1 validation error for Params
todos
  Input should be a valid list [type=list_type, input_value=[{"title": ...}], input_type=str]

The todos parameter is received as a JSON string instead of a parsed list/array.

Root Cause

In KimiToolset.handle() (kimi_cli/soul/toolset.py:301):

arguments: JsonType = json.loads(tool_call.function.arguments or "{}", strict=False)

The Moonshot API returns function.arguments with double-encoded JSON strings for nested array/object values. After json.loads parses the outer JSON, inner values like todos remain as strings:

API returns: {"todos": "[{\"title\": \"test\", \"status\": \"in_progress\"}]"}
                              ^--- string, not array!
json.loads → {"todos": "[{\"title\": \"test\", \"status\": \"in_progress\"}]"}
                        ^--- still a string!
Pydantic → ERROR: expected list, got str

Affected Tools

Tool Parameter Type
SetTodoList todos list[Todo]
ExitPlanMode options list[Option]
StrReplaceFile edit dict or list[Edit]
Any tool with array/dict params varies varies

Reproduction

  1. Call SetTodoList with todos=[{"title": "test", "status": "in_progress"}]
  2. Observe Pydantic validation error

Proposed Fix

In kimi_cli/soul/toolset.py, KimiToolset.handle() method, replace:

arguments: JsonType = json.loads(tool_call.function.arguments or "{}", strict=False)

with:

raw = tool_call.function.arguments or "{}"
if isinstance(raw, dict):
    arguments: JsonType = raw
else:
    arguments = json.loads(raw, strict=False)
    # Moonshot API quirk: double-encoded JSON strings
    if isinstance(arguments, dict):
        for k, v in list(arguments.items()):
            if isinstance(v, str) and v and v[0] in ("[", "{"):
                try:
                    arguments[k] = json.loads(v, strict=False)
                except (json.JSONDecodeError, ValueError):
                    pass

Environment

  • Kimi CLI: 1.46.0
  • Pydantic: 2.12.5
  • OpenAI SDK: 2.14.0
  • Python: 3.14
  • OS: Linux

Additional Context

The same issue exists in SimpleToolset.handle() in kosong/tooling/simple.py (the upstream library), but the Kimi CLI uses KimiToolset instead, which has its own handle() method with the same bug.

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/toolset.py at KimiToolset.handle(), then reproduce the SetTodoList call described in the issue and inspect the parsed arguments before Pydantic validation. Compare the behavior with the stated Moonshot double-encoding case; done means array and object parameters reach the affected tools as parsed values without validation errors.

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
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.