fix(ai): encode empty successful tool_result content as empty string
Strict Anthropic-compatible endpoints (Z.AI GLM at api.z.ai/api/anthropic) reject a whole request when a tool_result block carries content: [] , returning 400 code 1213 "The prompt parameter was not received normally". The official API accepts both shapes, so the empty array only surfaced on compatible endpoints once a tool returned empty output on a vision-capable model (text-only models already encode the joined empty string). Normalize the empty array to "" at encode time, alongside the existing error-placeholder normalization from #2250.
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Anthropic-compatible endpoints with strict prompt validation (e.g. Z.AI GLM `api.z.ai/api/anthropic`, which rejects the whole request with `400 code 1213 "The prompt parameter was not received normally"`) failing sessions once a tool returned empty output on a vision-capable model: empty successful `tool_result` blocks now encode as `content: ""` instead of `content: []`, which both the official API and strict compatible endpoints accept.
|
||||
|
||||
## [17.3.5] - 2026-08-16
|
||||
|
||||
### Added
|
||||
|
||||
@@ -3548,6 +3548,12 @@ function buildToolResultBlock(
|
||||
}
|
||||
content = content.filter(block => block.type === "text");
|
||||
}
|
||||
// An empty array is valid for the official API, but strict Anthropic-compatible
|
||||
// endpoints (Z.AI GLM: 400 code 1213 "The prompt parameter was not received
|
||||
// normally") reject it; the empty-string form is accepted by both.
|
||||
if (Array.isArray(content) && content.length === 0) {
|
||||
content = "";
|
||||
}
|
||||
content = ensureErrorToolResultWireContent(content, msg.isError);
|
||||
const block: ContentBlockParam = {
|
||||
type: "tool_result",
|
||||
|
||||
@@ -20,6 +20,13 @@ const anthropicModel: Model<"anthropic-messages"> = buildModel({
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
});
|
||||
|
||||
const visionModel: Model<"anthropic-messages"> = buildModel({
|
||||
...baseModel,
|
||||
input: ["text", "image"],
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
});
|
||||
|
||||
const user: UserMessage = {
|
||||
role: "user",
|
||||
content: "run the tool",
|
||||
@@ -95,4 +102,24 @@ describe("anthropic empty error tool_result encoding", () => {
|
||||
expect(block.is_error).toBe(false);
|
||||
expect(block.content).toBe("");
|
||||
});
|
||||
|
||||
it("encodes empty successful tool results as empty string, not empty array, on image-capable models", () => {
|
||||
// Regression: vision-capable models serialize whitespace-only successful
|
||||
// tool results as `content: []` on the wire. Official Anthropic accepts it,
|
||||
// but strict Anthropic-compatible endpoints (Z.AI GLM, api.z.ai/api/anthropic)
|
||||
// reject the whole request with 400 code 1213 ("The prompt parameter was
|
||||
// not received normally"). `content: ""` is accepted by both.
|
||||
const toolResult: ToolResultMessage = {
|
||||
role: "toolResult",
|
||||
toolCallId: "toolu_empty_error",
|
||||
toolName: "write",
|
||||
content: [{ type: "text", text: "" }],
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
const block = getToolResultBlock(visionModel, toolResult);
|
||||
expect(block.is_error).toBe(false);
|
||||
expect(block.content).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user