Fix empty tool name 400 errors from Claude API and missing @swc/helpers in Docker (#288)

* Initial plan

* fix: filter empty tool names and missing tool_use_id; add @swc/helpers to Docker

Co-authored-by: ngleichner1 <263653359+ngleichner1@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ngleichner1 <263653359+ngleichner1@users.noreply.github.com>
This commit is contained in:
Nina Gleichner
2026-03-10 22:46:17 +07:00
committed by GitHub
parent 54b1d8c8de
commit c2b31f6b20
3 changed files with 19 additions and 2 deletions

View File

@@ -29,6 +29,8 @@ RUN mkdir -p /app/data
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/.next/standalone ./
# Explicitly copy @swc/helpers — not always traced by standalone output but needed at runtime
COPY --from=builder /app/node_modules/@swc/helpers ./node_modules/@swc/helpers
COPY --from=builder /app/scripts/run-standalone.mjs ./run-standalone.mjs
COPY --from=builder /app/scripts/runtime-env.mjs ./runtime-env.mjs
COPY --from=builder /app/scripts/healthcheck.mjs ./healthcheck.mjs

View File

@@ -127,14 +127,24 @@ export function prepareClaudeRequest(body, provider = null) {
}
// Pass 1.4: Filter out tool_use blocks with empty names (causes Claude 400 error)
// Apply to ALL roles (assistant tool_use + any user messages that may carry tool_use)
// Also filter tool_result blocks with missing tool_use_id
for (const msg of filtered) {
if (msg.role === "assistant" && Array.isArray(msg.content)) {
if (Array.isArray(msg.content)) {
msg.content = msg.content.filter(
(block) => block.type !== "tool_use" || (block.name && block.name.trim())
(block) => block.type !== "tool_use" || (block.name && block.name?.trim())
);
msg.content = msg.content.filter(
(block) => block.type !== "tool_result" || block.tool_use_id
);
}
}
// Also filter top-level tool declarations with empty names
if (body.tools && Array.isArray(body.tools)) {
body.tools = body.tools.filter((tool) => tool.name && tool.name?.trim());
}
// Pass 1.5: Fix tool_use/tool_result ordering
// Each tool_use must have tool_result in the NEXT message (not same message with other content)
filtered = fixToolUseOrdering(filtered);

View File

@@ -175,6 +175,9 @@ export function openaiToClaudeRequest(model, body, stream) {
};
});
// Filter out tools with empty names (would cause Claude 400 error)
result.tools = result.tools.filter((tool) => tool.name && tool.name?.trim());
// Add cache_control to last tool that doesn't have defer_loading
// Tools with defer_loading=true cannot have cache_control (API rejects it)
for (let i = result.tools.length - 1; i >= 0; i--) {
@@ -227,6 +230,8 @@ function getContentBlocksFromMessage(msg, toolNameMap = new Map(), disableToolPr
if (part.type === "text" && part.text) {
blocks.push({ type: "text", text: part.text });
} else if (part.type === "tool_result") {
// Skip tool_result with no tool_use_id (would be useless and may cause errors)
if (!part.tool_use_id) continue;
blocks.push({
type: "tool_result",
tool_use_id: part.tool_use_id,