Skip to content

[BUG MODEL]: zai-glm-5-3-flash drops the last property of large tool-call arguments #653

Open

Description

@lu-zero

Model

zai-glm-5-3-flash (GLM 5.3 Flash hosted by Mistral); not reproducible on mistral-large-latest

Summary

When GLM 5.3 Flash emits a tool call whose arguments object is large (~900+ chars), the last property of the arguments object is dropped. The truncated arguments still parse as valid JSON, so callers only notice when a required-by-contract parameter is missing. The same request against the same model served by Z.AI directly, or against mistral-large-latest, keeps all properties.

In our agent (an open-source coding agent that mounts ~20 tools), this surfaced as GLM-5.3-Flash-via-Mistral dropping the last argument of large tool calls across several tools, while the identical sessions run against the same model on Z.AI's own endpoint never dropped an argument.

Minimal reproduction

Requires mistralai>=3.7 and MISTRAL_API_KEY. The tool schema mirrors a real tool from our agent (a persistent-notes tool with command/path/content/tags). The user message asks the model to record ~900 chars of state.

#!/usr/bin/env python3
import json
from mistralai.client import Mistral

MEMORY_SCHEMA = {
    "type": "object",
    "properties": {
        "command": {
            "type": "string",
            "enum": ["list", "read", "write", "delete"],
            "description": "- list [tags]: tag-grouped index.\n- read path|tags: one body.\n- write path tags content: create or overwrite a note.\n- delete path",
        },
        "path": {"type": "string", "description": "Relative path, e.g. 'architecture.md'."},
        "content": {"type": "string", "description": "Body for write (frontmatter added automatically)."},
        "tags": {"type": "array", "items": {"type": "string"}, "description": "snake_case tags."},
    },
    "required": ["command"],
}

TOOLS = [{"type": "function", "function": {"name": "memory", "description": "Persistent scratchpad notes.", "parameters": MEMORY_SCHEMA}}]

BODY = "\n".join([
    "# FLINT-TT-DENSE-MLP-STAGING: diagnose done, fix owed (2026-11-01)",
    "- Branch row/FLINT-TT-DENSE-MLP-STAGING, head 5723a65",
    "- Worktree /tmp/flint-p150-attn, build /tmp/build-p150staging",
    "- W0 band gate allocation-order sensitive: per-tensor rel_rms of dense gate/up/down staging",
    "- MatmulWeight (flint_tt.cpp ~538) is the dispatch seam: block weights route vt::MatmulBT",
    "- DeviceAttnEnabled() reads VT_FLINT_TT_DEVICE_ATTN; DumpOn() reads FLINT_TT_W0_DUMP",
    "- DBuf pooled, move-only RAII (dense_device_glue.h:144); pool policy memoized per device",
    "- BuildFlintTTDeviceContext stages resident slice, throws if plan.resident_bytes mismatch",
    "- CPU shadow dumps behind FLINT_CPU_DUMP; TT arm stage dumps behind FLINT_TT_W0_DUMP",
    "- Test harness: tests/vllm/models/test_flint_tt_w3_plain.cpp, 5-arm compute-kernel sweep",
    "- Next site to chase: dense gate/up staging at L0 and L46 under reversed staging order",
])

MESSAGES = [
    {"role": "system", "content": "You are a coding agent. Save important state to memory before finishing a task. Always use the memory tool with command=write."},
    {"role": "user", "content": "We just finished diagnosing the dense-MLP staging issue. Save the session state to memory now, then stop. Here is what to record:\n" + BODY},
]

client = Mistral(api_key="...")
res = client.chat.complete(model="zai-glm-5-3-flash", messages=MESSAGES, tools=TOOLS, temperature=0.0)
for choice in res.choices:
    for tc in choice.message.tool_calls or []:
        args = json.loads(tc.function.arguments)
        print(sorted(args.keys()))

Output

temperature=0, so this is deterministic. 25 consecutive runs: the arguments always come back as valid JSON with exactly three keys:

['command', 'content', 'tags']

path — the last property in the schema — is always missing. Raw arguments begin:

{"command": "write", "content": "# FLINT-TT-DENSE-MLP-STAGING: diagnose done, fix owed (2026-11-01)\n- Branch row/FLINT-TT-DENSE-MLP-STAGING, head 5723a65\n- Worktree /tmp/flint-p150-attn, build /tmp/

finish_reason: tool_calls, no truncation of content itself, and completion_tokens is consistent with a complete call.

Expected output

['command', 'content', 'path', 'tags']

Controls that isolate the cause

Endpoint Model Calls with all 4 keys
Mistral chat.complete zai-glm-5-3-flash 0/10
Mistral chat.complete (streaming) zai-glm-5-3-flash 0/15
Mistral chat.complete mistral-large-latest 6/6
Z.AI direct (https://api.z.ai/api/paas/v4/chat/completions), byte-identical request glm-5.3-flash 8/8

The same failure pattern also appears in a production agent against other tool schemas on this model (a read(offset, limit, path)-shaped tool lost path, a code-execution tool lost code), always the last property, always on arguments of roughly 1–4 KB. We could not reproduce the raw-truncation symptom (arguments that fail to parse as JSON) in isolation — a 3 KB single tool call streamed fine — so the reliably reproducible symptom is the dropped last property on ~1 KB argument objects.

Additional context

Suggested solutions

  • Check the tool-call arguments assembly for hosted third-party models (GLM) — a buffer/segment boundary at roughly the arguments length where the last property gets cut, or a stop-sequence/parse step that treats the tail of a large arguments object as prose.
  • If a fix takes a while, documenting the workaround (mark practical-requirements as required in the schema) would help other agent builders.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions