openai-base: a tool call cut off by finish_reason "length" is reported as "tool_calls" and re-sent
メンテナーはふだん 1 日以内に返信
@tombeckenham がすでに取り組んでいます。
2026年10月22日 から。
評価
この issue はまだ評価されていません。
説明
TanStack AI version
@tanstack/ai 0.71.0, @tanstack/ai-openai 0.31.0 (@tanstack/openai-base 0.16.0)
Framework/Library version
Node.js 22, chat() on the server, no framework
Describe the bug and the steps to reproduce it
With the Chat Completions adapter, when max_tokens runs out in the middle of a tool call's arguments, the stream
ends with finish_reason: "length". chat() then:
- emits
TOOL_CALL_ENDfor the unfinished arguments andRUN_FINISHEDwithfinishReason: "tool_calls", so the
token limit is lost; - treats the arguments as a tool parse failure (
TOOL_CALL_RESULT:Failed to parse tool arguments as JSON) and
sends the conversation back to the model. With the samemax_tokensthe next answer is usually cut off too; in the
repro onechat()call sends 5 requests.
The finish is chosen at
packages/openai-base/src/adapters/chat-completions-text.ts#L1392-L1399:
emittedAnyToolCallEnd wins over the pending length. For plain text the adapter already passes length through.
Expected: finishReason: "length" (the arguments are incomplete because of the limit, not malformed), and no
automatic retry of a call the limit cut off.
Steps: save the script below as repro.mjs, then
npm init -y && npm pkg set type=module
npm install @tanstack/[email protected] @tanstack/[email protected]
node repro.mjs
Output:
RUN_FINISHED tool_calls
TOOL_CALL_RESULT {"error":"Failed to parse tool arguments as JSON: {\"title\":\"Pottery workshop in Mál"}
(the two lines above repeat five times)
requests: 5
Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
No API key is needed: a custom fetch returns the stream.
import { chat, toolDefinition } from '@tanstack/ai';
import { createOpenaiChatCompletions } from '@tanstack/ai-openai';
// The arguments stop mid-string because max_tokens ran out; the API says so with finish_reason "length".
const chunk = (delta, finish_reason = null) => ({ id: 'chatcmpl-1', object: 'chat.completion.chunk', created: 1,
model: 'gpt-6-luna', choices: [{ index: 0, delta, finish_reason }] });
const body = [
chunk({ role: 'assistant', tool_calls: [{ index: 0, id: 'call_1', type: 'function',
function: { name: 'create_event', arguments: '{"title":"Pottery workshop in Mál' } }] }),
chunk({}, 'length'),
].map(value => `data: ${JSON.stringify(value)}\n\n`).join('') + 'data: [DONE]\n\n';
let requests = 0;
globalThis.fetch = async () => {
requests += 1;
return new Response(body, { headers: { 'content-type': 'text/event-stream' } });
};
const createEvent = toolDefinition({
name: 'create_event', description: 'Create a calendar event',
inputSchema: { type: 'object', properties: { title: { type: 'string' } }, required: ['title'] },
});
for await (const event of chat({
adapter: createOpenaiChatCompletions('gpt-6-luna', 'test-key'),
messages: [{ role: 'user', content: 'Create an event for a pottery workshop in Málaga.' }],
tools: [createEvent],
modelOptions: { max_tokens: 12 },
debug: false,
})) {
if (event.type === 'RUN_FINISHED') console.log('RUN_FINISHED', event.metadata?.tanstack?.finishReason);
if (event.type === 'TOOL_CALL_RESULT') console.log('TOOL_CALL_RESULT', event.content);
}
console.log('requests:', requests);
Terms & Code of Conduct
- I agree to follow this project's Code of Conduct
- I understand that if my bug cannot be reliably reproduced in a debuggable environment, it will probably not be
fixed and this issue may even be closed.
- 主要言語
- TypeScript
- スター
- 3.2k
- フォーク
- 365
- 平均マージ
- 1日 9時間
- マージ済み PR(30日)
- 263
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
TanStack/ai のほかの issue
-
ai-ollama: streamed chat() reports finishReason "stop" when done_reason is "length"対応中かも @AlemTuzlak が今日担当しました。 オープンwaiting-on: maintainer
メンテナーはふだん 1 日以内に返信
-
ai-vercel-gateway: surface provider_metadata.gateway cost on usage (usage.cost)対応中かも @tombeckenham が 1 日前に担当しました。 オープンwaiting-on: maintainer
メンテナーはふだん 1 日以内に返信
-
withPersistence does not save activity rows when a run is stopped or fails対応中かも @tombeckenham が 6 日前に担当しました。 オープンwaiting-on: maintainer
メンテナーはふだん 1 日以内に返信
-
Discriminate status-dependent record and option types (RunRecord, SessionIndexEntry, InputState, durable-only options)対応中かも @jherr が 6 日前に担当しました。 オープンenhancement waiting-on: maintainer
メンテナーはふだん 1 日以内に返信
-
ai-harness: an input with no principal runs with the owner session's principal対応中かも @tombeckenham が 7 日前に担当しました。 オープンwaiting-on: maintainer
メンテナーはふだん 1 日以内に返信
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
Automattic/data-liberation-agent#953 ·
メンテナーはふだん 1 日以内に返信
-
documentation
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
funmary-app/funmary#399 ·
メンテナーはふだん 1 日以内に返信
-
area:curator enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
CassioDalla/Scrinalia#150 ·
メンテナーはふだん 1 日以内に返信
-
status: needs-triage v1-bug-bash
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
a2ui-project/a2ui#3533 ·
メンテナーはふだん 1 日以内に返信
-
backend enhancement priority:P3
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
geolens-io/geolens#3310 ·
メンテナーはふだん 1 日以内に返信