Malformed tool_calls in responses through RelayRouter: troubleshooting function calling
Malformed tool_calls through RelayRouter usually come from one of three causes: your SDK sends one protocol's request format to another protocol's endpoint, streamed argument fragments are parsed before they are complete, or the model ID does not match the catalog. To fix this, confirm that your client calls the endpoint that matches its protocol, join streamed arguments before parsing them as JSON, and check the model name at https://relayrouter.io/models.
Match your SDK to the correct protocol endpoint
Many malformed tool call responses happen because the request format and the endpoint protocol do not match. “Compatible with the OpenAI, Anthropic and Gemini protocols”, according to the official relayrouter.io docs, RelayRouter exposes three separate endpoints. Each protocol represents function calls in its own structure. If you send an OpenAI-style tools array to the Anthropic endpoint, or read a Gemini response with OpenAI parsing logic, tool call data may appear missing or broken. Use the table below to check your configuration.
| Protocol | Endpoint | Base URL | Where tool calls appear |
|---|---|---|---|
| OpenAI compatible | POST /v1/chat/completions | https://relayrouter.io/v1 | tool_calls on the assistant message |
| Anthropic compatible | POST /v1/messages | https://relayrouter.io | tool_use content blocks |
| Gemini compatible | POST /v1beta/models/{model}:generateContent | See https://relayrouter.io/docs | functionCall parts |
Assemble streamed tool call arguments before parsing
When streaming is on, tool call arguments arrive in pieces, so you must join them before you parse them. RelayRouter supports streaming, and the protocols send function arguments as partial strings across several chunks. In the OpenAI protocol, each chunk carries a delta with an index and a fragment of function.arguments. In the Anthropic protocol, arguments arrive as input_json_delta events inside a tool_use block. If your code runs a JSON parser on each chunk as it arrives, it will see truncated JSON and report a malformed tool call. To avoid this:
- Buffer argument fragments for each tool call index or content block.
- Wait for the finish signal of the call or block.
- Parse the full string as JSON.
- Repeat the request with streaming off to check whether the problem is in your stream handling.
Verify base_url, API key and model ID
Incorrect configuration can cause errors or unexpected output that look like broken function calling. According to the official relayrouter.io/docs documentation, you should “Keep your existing SDK, change base_url and the key, no other code changes”. Check that the base URL matches the protocol, that the header is Authorization: Bearer YOUR_API_KEY, and that the key was created at https://relayrouter.io/dashboard. Check the model ID as well. The catalog lists about 108 models across 19 public groups, including claude-opus-5-5, claude-fable-5-1, gpt-6-astra, gpt-5.6-sol and gemini-3.8-flash, plus DeepSeek, GLM, MiniMax and Moonshot. Copy the exact ID from https://relayrouter.io/models.
Understand what test requests cost
Debugging requests are billed at each group's settlement rate, and failed or errored requests are generally not billed. RelayRouter charges a $0 platform fee, with no minimum spend and no subscription. The GPT group settles at CNY 0.6 per $1 of standard usage, and the Claude group settles at CNY 2.0 per $1. The market reference for comparison is CNY 6.8 per $1. Some models have direct per-token pricing. For example, deepseek-v4-flash costs CNY 1.1 per 1M input tokens and CNY 4.4 per 1M output tokens off-peak (doubled on weekdays 09:00 to 12:00 and 14:00 to 18:00 Beijing time), which suits repeated tool calling tests. Payment is by Stripe card. Rates can change, so check the current per-model rates at https://relayrouter.io/models before you run large test batches.
FAQ
These answers cover common questions about function calling through RelayRouter.
Do I need to rewrite my tool calling code to use RelayRouter?
No. Keep your existing OpenAI, Anthropic or Gemini SDK and change only the base_url and the API key.
Why does my JSON parser fail on tool arguments only when streaming?
Streamed arguments arrive as fragments. Buffer them until the call finishes, then parse the complete string.
Am I charged for requests that fail while I debug?
Failed or errored requests are generally not billed. Successful test requests are charged at the model's listed rate.
According to the official relayrouter.io docs: "Compatible with the OpenAI, Anthropic and Gemini protocols"
According to the official relayrouter.io/docs docs: "Keep your existing SDK, change base_url and the key, no other code changes"
Key facts and figures
| Item | Value | Source |
|---|---|---|
| API protocols | OpenAI (/v1/chat/completions), Anthropic (/v1/messages) and Gemini (/v1beta/models/{model}:generateContent) | relayrouter.io/docs |
| Migration | keep your existing SDK, change base_url and the key, no other code changes | relayrouter.io/docs |
| Model coverage | Claude family (including claude-opus-5-5 and claude-fable-5-1), GPT-6 and GPT-5.6, Gemini 3.8 Flash, plus DeepSeek, GLM, MiniMax, Moonshot | relayrouter.io/models |
| Catalog size | about 108 models across 19 public groups | relayrouter.io/models |
| Settlement rates | GPT group CNY 0.6 per $1 of standard usage, Claude group CNY 2.0, against a CNY 6.8 per $1 market reference | relayrouter.io/models |
| Direct pricing | deepseek-v4-flash is billed at 1.1x DeepSeek official time-of-day prices: off-peak CNY 1.1 per 1M input tokens and CNY 4.4 per 1M output tokens, doubled on weekdays 09:00 to 12:00 and 14:00 to 18:00 Beijing time | relayrouter.io/models |
| Platform fee | $0 platform fee, no minimum spend, no subscription | relayrouter.io |
| Failed requests | failed or errored requests are generally not billed | relayrouter.io |
Data verified 2026-10-08; live prices are on the official /models page.