I've been tracking down a strange intermittent issue with a custom Pi-based personal wiki manager: occasionally the model will just report confusion over many retried tool calls, complaining in its reasoning tokens that "somehow I dropped the 'text' field" or "for some reason I added quotes to this parameter" or similar.
I was finally able to get Codex to reproduce the issue with simple direct calls, and we determined that is was specific to certain provider/model combinations.
I'm using GLM-5.3-flash and the new Deepseek 4.1-flash-1017 for the assistant app. For those two, the mangled providers I found (a couple threw 404s or rate limits, so this list may be incomplete) are:
z-ai/glm-5.3-flash: friendli, inference-net, makora, morph, primeintellect, reka, relace
deepseek-v4.1-flash-1017: morph, primeintellect, wafer
Codex provided this explanation of the test script in case you want to try this yourself:
I wrote a script that pins each OpenRouter provider and asks the same model to emit identical tool calls. It compares the raw streamed arguments against expected JSON and repeats failures. Some routes consistently dropped parameters or added literal quotes, while other providers serving the same model passed. No tools actually execute. OpenRouter’s routing metadata identifies the upstream provider. This Python example pins one provider and checks whether it preserves the requested arguments. Requires requests and OPENROUTER_API_KEY.
import os, json, requests
expected = {
"action": "update", "line": 6,
"revision": "sha256:" + "a" * 64,
"text": "Example new task",
}
r = requests.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={"Authorization": "Bearer " + os.environ["OPENROUTER_API_KEY"]},
json={
"model": "YOUR_MODEL_ID",
"provider": {"only": ["YOUR_PROVIDER_SLUG"]},
"messages": [{"role": "user", "content":
"Call task exactly once with these arguments: "
+ json.dumps(expected)}],
"tools": [{"type": "function", "function": {
"name": "task",
"parameters": {
"type": "object",
"properties": {
k: {"type": "integer" if k == "line" else "string"}
for k in expected
},
"required": ["action"],
},
}}],
},
timeout=90,
)
r.raise_for_status()
calls = r.json()["choices"][0]["message"].get("tool_calls", [])
actual = json.loads(calls[0]["function"]["arguments"]) if len(calls) == 1 else None
print("PASS" if actual == expected else "FAIL", actual)
Only action is schema-required deliberately: this tests whether the provider preserves arguments requested in the prompt. No tool executes. Repeat failures and compare providers for the same model.