litellm - 💡(How to fix) Fix OpenAI-proxy streaming for Claude breaks AI SDK 6.x multi-step tool calls: "text part {id} not found" [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#26529Fetched 2026-04-26 05:06:30
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Participants
Timeline (top)
labeled ×1

When using LiteLLM's OpenAI-compatible proxy (/v1/chat/completions) with a Claude model and the Vercel AI SDK 6.x ([email protected]), multi-step tool calls produce streaming errors of the form:

text part chatcmpl-<id> not found

This causes the AI SDK to silently drop text deltas for the affected steps — producing empty or truncated assistant responses.

Error Message

if (part.type === "text-delta") { const activeText = activeTextContent[part.id]; if (activeText == null) { controller.enqueue({ part: { type: "error", error: text part ${part.id} not found } }); return; // delta is silently dropped } }

Root Cause

AI SDK 6.x tracks streaming text parts by ID using a activeTextContent[part.id] map. The flow is:

  • text-start { id: "0" } → registers the text part
  • text-delta { id: "0", delta: "..." } → appends to registered part
  • text-end { id: "0" } → cleans up

The @ai-sdk/openai provider uses id: "0" for all text events in a single step. However, when LiteLLM proxies a multi-step Claude response (where Claude uses tools and then continues with text), the translated stream appears to emit a text-delta with an ID matching the OpenAI chatcmpl-<id> response-level field, which was never registered via text-start. This causes the AI SDK to drop the delta and emit an error.

The error in AI SDK source (ai/dist/index.mjs):

if (part.type === "text-delta") {
  const activeText = activeTextContent[part.id];
  if (activeText == null) {
    controller.enqueue({ part: { type: "error", error: `text part ${part.id} not found` } });
    return; // delta is silently dropped
  }
}

Fix Action

Workaround

Use @ai-sdk/anthropic natively instead of routing Claude through the LiteLLM OpenAI proxy. This bypasses the translation layer entirely and the "text part not found" errors disappear.

import { createAnthropic } from '@ai-sdk/anthropic'
const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })

// Route claude-* models directly to Anthropic, LiteLLM for everything else
function resolveModel(modelId: string) {
  if (modelId.startsWith('claude-')) return anthropic(modelId)
  return litellm(modelId)
}

Code Example

text part chatcmpl-<id> not found

---

const litellm = createOpenAI({ baseURL: 'https://<litellm>/v1', apiKey: '...' })

---

const result = streamText({
     model: litellm('claude-sonnet-4-6'),
     tools: { searchEvidence: ..., webSearch: ... },
     stopWhen: stepCountIs(20),
   })

---

{ type: "error", errorText: "text part chatcmpl-<id> not found" }

---

if (part.type === "text-delta") {
  const activeText = activeTextContent[part.id];
  if (activeText == null) {
    controller.enqueue({ part: { type: "error", error: `text part ${part.id} not found` } });
    return; // delta is silently dropped
  }
}

---

import { createAnthropic } from '@ai-sdk/anthropic'
const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })

// Route claude-* models directly to Anthropic, LiteLLM for everything else
function resolveModel(modelId: string) {
  if (modelId.startsWith('claude-')) return anthropic(modelId)
  return litellm(modelId)
}
RAW_BUFFERClick to expand / collapse

Description

When using LiteLLM's OpenAI-compatible proxy (/v1/chat/completions) with a Claude model and the Vercel AI SDK 6.x ([email protected]), multi-step tool calls produce streaming errors of the form:

text part chatcmpl-<id> not found

This causes the AI SDK to silently drop text deltas for the affected steps — producing empty or truncated assistant responses.

Environment

  • LiteLLM version: 1.x (confirmed on hosted proxy at llm.behaviorlabs.ai/v1)
  • Model: claude-sonnet-4-6 (via LiteLLM Claude alias)
  • Client: @ai-sdk/[email protected] pointing at LiteLLM proxy
  • AI SDK: [email protected]
  • Framework: Next.js App Router, streamText + stopWhen: stepCountIs(20) for multi-step tool use

Steps to Reproduce

  1. Configure @ai-sdk/openai to point at LiteLLM proxy:
    const litellm = createOpenAI({ baseURL: 'https://<litellm>/v1', apiKey: '...' })
  2. Call streamText with tools and stopWhen: stepCountIs(20) using a Claude model:
    const result = streamText({
      model: litellm('claude-sonnet-4-6'),
      tools: { searchEvidence: ..., webSearch: ... },
      stopWhen: stepCountIs(20),
    })
  3. Ask a question that triggers tool use followed by a text response (multi-step)
  4. Observe: the final text response is empty OR the stream emits an error event:
    { type: "error", errorText: "text part chatcmpl-<id> not found" }

Root Cause

AI SDK 6.x tracks streaming text parts by ID using a activeTextContent[part.id] map. The flow is:

  • text-start { id: "0" } → registers the text part
  • text-delta { id: "0", delta: "..." } → appends to registered part
  • text-end { id: "0" } → cleans up

The @ai-sdk/openai provider uses id: "0" for all text events in a single step. However, when LiteLLM proxies a multi-step Claude response (where Claude uses tools and then continues with text), the translated stream appears to emit a text-delta with an ID matching the OpenAI chatcmpl-<id> response-level field, which was never registered via text-start. This causes the AI SDK to drop the delta and emit an error.

The error in AI SDK source (ai/dist/index.mjs):

if (part.type === "text-delta") {
  const activeText = activeTextContent[part.id];
  if (activeText == null) {
    controller.enqueue({ part: { type: "error", error: `text part ${part.id} not found` } });
    return; // delta is silently dropped
  }
}

Expected Behavior

The streaming translation from Anthropic's native SSE format to OpenAI's format should preserve consistent part IDs across all events within a step, so that text-start/text-delta/text-end events all carry the same ID.

Workaround

Use @ai-sdk/anthropic natively instead of routing Claude through the LiteLLM OpenAI proxy. This bypasses the translation layer entirely and the "text part not found" errors disappear.

import { createAnthropic } from '@ai-sdk/anthropic'
const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })

// Route claude-* models directly to Anthropic, LiteLLM for everything else
function resolveModel(modelId: string) {
  if (modelId.startsWith('claude-')) return anthropic(modelId)
  return litellm(modelId)
}

Additional Notes

  • The issue only manifests in multi-step tool call sequences (the first step usually works fine)
  • Affects any AI SDK 6.x consumer that uses LiteLLM as an OpenAI-compatible proxy for Claude
  • Non-fatal in scenarios where text still arrives despite the error, but causes empty responses in some cases

extent analysis

TL;DR

To fix the "text part not found" error when using LiteLLM's OpenAI-compatible proxy with the Vercel AI SDK 6.x, use the @ai-sdk/anthropic provider natively for Claude models instead of routing them through the LiteLLM OpenAI proxy.

Guidance

  • Identify if your application uses the LiteLLM OpenAI proxy with Claude models and the Vercel AI SDK 6.x.
  • If using multi-step tool calls, consider the workaround of using @ai-sdk/anthropic natively for Claude models.
  • Verify that the issue is resolved by checking for the absence of "text part not found" errors and ensuring that text responses are complete.
  • Be aware that this issue only affects multi-step tool call sequences and may not be fatal in all scenarios.

Example

import { createAnthropic } from '@ai-sdk/anthropic'
const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })

function resolveModel(modelId: string) {
  if (modelId.startsWith('claude-')) return anthropic(modelId)
  return litellm(modelId)
}

Notes

This workaround bypasses the translation layer entirely and may have implications for applications that rely on the LiteLLM OpenAI proxy for other models or features.

Recommendation

Apply the workaround by using @ai-sdk/anthropic natively for Claude models, as it bypasses the translation layer and resolves the "text part not found" errors.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING