A GLM request succeeds, the model asks to use a function, and the next request fails with HTTP 400.

This pattern deserves a different investigation from a failed first request. The connection may already work; the problem can be in the conversation history sent back after tool execution.

Common candidates include a missing assistant tool-call message, a mismatched tool_call_id, incorrectly encoded arguments, or discarded fields required by the model's thinking behavior.

This guide builds a small two-turn test for a GLM proxy route. It uses a synthetic stock lookup so you can inspect the complete exchange without touching a production database.

1. Confirm that the failure begins on the second request

Record the exact failing stage.

StageFirst investigation
Initial request failsCredentials, model ID, endpoint, and request parameters
Model returns no tool callTool description, model behavior, and supported capabilities
Local function failsArgument validation and application logic
Tool result submission failsMessage history, call IDs, and required fields
Final answer is wrongTool result interpretation and task instructions

HTTP 400 does not identify one universal cause. Read the provider's actual error message and preserve a sanitized request record.

Avoid changing the API key repeatedly when the same key already produced the first valid response.

For basic connection setup, use the existing GLM-5.3 integration guide. This tutorial starts at the multi-turn boundary.

2. Keep the tool call and its result connected

The Z.AI Function Calling guide documents the model's tool-call ID, function name, and arguments.

For the Chat Completions workflow used here, preserve the assistant message that requested the tool. Then append a tool result referring to that same call ID.

The intended sequence is:

User request
Assistant message containing tool_calls
Tool result with the matching tool_call_id
Next model request using the accumulated messages

Do not replace the assistant message with a plain-text summary such as “the model requested stock.”

That removes the structured relationship the next request needs.

Also, this example is specifically for Chat Completions. A Responses API client has a different request and result structure.

3. Preserve model-required history fields

GLM thinking behavior can affect tool conversations.

The official thinking-mode documentation instructs developers to preserve the relevant thinking blocks when using interleaved thinking with tools.

That creates an important implementation distinction: a field may be required for protocol continuity even when you do not display it in the user interface.

Do not rebuild the assistant message using only its visible text. Preserve the required fields according to the selected model and route.

The example below serializes the SDK message, including available extension fields, instead of manually keeping only content.

If a gateway strips a field that the upstream model requires, that is a compatibility issue to investigate—not a reason to invent a replacement value.

4. Configure a small GLM test

Use a GLM model whose route supports function calling, such as an available GLM-5.3 route.

Install the client and set your credentials:

python -m pip install openai

export TREEROUTER_API_KEY="YOUR_TREEROUTER_API_KEY"
export GLM_MODEL="EXACT_GLM_MODEL_ID_FROM_YOUR_ACCOUNT"

Confirm the model ID and capability in your account. The public TreeRouter tool-calling reference provides the gateway-side interface documentation.

The test uses one fictional SKU, DEMO-42, with a stock value of 7. It does not query or modify real inventory.

5. Run the two-turn probe

import json
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["TREEROUTER_API_KEY"],
    base_url="https://treerouter.com/v1",
    timeout=60.0,
    max_retries=0,
)

model = os.environ["GLM_MODEL"]

tools = [{
    "type": "function",
    "function": {
        "name": "lookup_stock",
        "description": "Read stock from the synthetic test inventory.",
        "parameters": {
            "type": "object",
            "properties": {"sku": {"type": "string"}},
            "required": ["sku"],
            "additionalProperties": False,
        },
    },
}]

messages = [{
    "role": "user",
    "content": (
        "Use lookup_stock to check DEMO-42. "
        "Then report its stock quantity. Do not guess."
    ),
}]

first = client.chat.completions.create(
    model=model,
    messages=messages,
    tools=tools,
    tool_choice="auto",
)

choice = first.choices[0]
message = choice.message
calls = message.tool_calls or []

if choice.finish_reason not in {"tool_calls", "stop"}:
    raise RuntimeError("The first generation was incomplete")

if not calls:
    raise RuntimeError("No tool call returned; this test is incomplete")

if len(calls) > 4:
    raise RuntimeError("Tool-call budget exceeded")

call_ids = [call.id for call in calls]
if any(not value for value in call_ids) or len(set(call_ids)) != len(call_ids):
    raise ValueError("Missing or duplicate tool-call IDs")

messages.append(message.model_dump(exclude_none=True))

for call in calls:
    if call.function.name != "lookup_stock":
        raise ValueError("Unexpected function")

    arguments = json.loads(call.function.arguments)
    if arguments != {"sku": "DEMO-42"}:
        raise ValueError("Unexpected arguments for the test fixture")

    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps({"sku": "DEMO-42", "stock": 7}),
    })

second = client.chat.completions.create(
    model=model,
    messages=messages,
    tools=tools,
    tool_choice="auto",
)

final = second.choices[0]
if final.message.tool_calls:
    raise RuntimeError("The model requested another round; inspect the result")

if final.finish_reason != "stop" or not (final.message.content or "").strip():
    raise RuntimeError("No completed final text answer")

print(final.message.content)

Check that the final answer reports the fixture's actual quantity: 7.

The script deliberately supports one round of tool execution. Another tool request is treated as an incomplete probe, not proof that the provider is broken.

A production agent may need a bounded loop with additional rounds, execution deadlines, and application-specific policies.

6. Compare the successful and failing histories

If the second request fails, inspect the messages you actually transmitted.

Check these relationships:

  • Every tool result refers to an ID from the preceding assistant tool-call message.
  • Every requested tool call receives the appropriate result before the next model request.
  • Function arguments are decoded before application validation.
  • Tool result content is encoded in the format expected by the endpoint.
  • Required model-specific history fields remain present.
  • The same intended model route is used for both requests.

Do not log API keys. Use synthetic inputs when sharing a reproduction.

The GLM Chat Completion reference helps confirm the current message and response fields.

7. Test failures without hiding them

Add controlled variations after the successful probe.

Use an unknown SKU and return an explicit application error rather than substituting stock zero.

Test malformed arguments in your local validation code without executing a real inventory operation.

Test a repeated tool call and decide how the application should handle it. For read-only lookup, repetition may mainly increase latency. For a payment or update operation, repetition can create a duplicate side effect.

A tool-call ID links messages inside a conversation. It should not automatically be treated as a business-wide idempotency key.

For operations that change external state, define deduplication at the business operation layer.

8. Avoid masking an incompatible route with retries

Repeatedly submitting the same invalid history usually does not repair it.

Keep automatic retries limited while diagnosing a reproducible 400 error. Correct the message structure or unsupported parameter first.

If a direct route succeeds while a gateway route fails, compare the documented capabilities and the fields sent through each path. Keep the model and test input as consistent as possible.

Do not conclude that every model or every proxy is affected from one failing combination.

Validate one complete tool cycle

A useful integration milestone includes a valid tool request, checked arguments, a correctly linked result, and a final answer consistent with the tool's data.

To evaluate a gateway route, create a TreeRouter account, confirm GLM tool-calling availability, and run the synthetic probe before connecting real business functions.

Keep the successful exchange as a regression fixture when changing the model, SDK, or API route.