Skip to content

StreamText error handling still not working properly in v4.0.18 #4099

Closed

Description

@samihamine

Description

Despite the fix in #3987, error handling in streamText is still not working as expected in version 4.0.18. The application crashes instead of properly catching and handling API errors, even with proper try/catch blocks in place.

Code example

Current Behavior

When an API error occurs (e.g., 404 Not Found), the error bypasses all error handling mechanisms and crashes the application with an uncaught exception, despite having proper error handling in place.

Error Log

APICallError [AI_APICallError]: Not Found
    at /app/node_modules/@ai-sdk/provider-utils/src/response-handler.ts:72:16
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async postToApi (/app/node_modules/@ai-sdk/provider-utils/src/post-to-api.ts:81:28)
    at async OpenAIChatLanguageModel.doStream (/app/node_modules/@ai-sdk/openai/src/openai-chat-language-model.ts:375:50)
    at async fn (/app/node_modules/ai/core/generate-text/stream-text.ts:532:25)
    at async /app/node_modules/ai/core/telemetry/record-span.ts:18:22
    at async _retryWithExponentialBackoff (/app/node_modules/ai/util/retry-with-exponential-backoff.ts:37:12)
    at async streamStep (/app/node_modules/ai/core/generate-text/stream-text.ts:487:15)
    at async fn (/app/node_modules/ai/core/generate-text/stream-text.ts:991:9)
    at async /app/node_modules/ai/core/telemetry/record-span.ts:18:22 {
  cause: undefined,
  url: XXXXX
  ....

Minimal Reproduction Code

import { streamText, StreamTextResult } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';

async function chatWithAPI(): Promise<StreamTextResult> {
    try {
        const apiProvider = createOpenAI({
            name: 'custom-api',
            apiKey: 'dummy-key',
            baseURL: 'http://api.example.com/v1', // Intentionally wrong URL to trigger 404
        });

        const result = streamText({
            model: apiProvider('model-id'),
            messages: [
                {
                    role: 'user',
                    content: 'Hello'
                }
            ],
            onFinish: async ({ text }) => {
                console.log('Finished:', text);
            },
            async onChunk(chunk) {
                if ('error' in chunk) {
                    console.error('Chunk error:', chunk.error);
                    throw new Error(API Error: ${ chunk.error });
                }
            },
            maxRetries: 2
        });
        return result;
    } catch (error) {
        console.error('Error caught:', error);
        throw new Error('Failed to process request');
    }
}

Environment

  • ai version: 4.0.18
  • @ai-sdk/openai version : 1.0.8
  • Node.js version: 18.x

AI provider

@ai-sdk/openai version : 1.0.8

Additional context

This issue was supposedly fixed in #3987, but the problem persists in the latest version (4.0.18). The error handling mechanisms (try/catch blocks) are not catching the errors from the streaming process, causing the application to crash unexpectedly.

Expected Behavior

Errors should be properly caught either:

  1. In the try/catch block
  2. Or propagated as a proper error response through the stream

Would appreciate guidance on whether this is a known limitation or if there's a workaround available.

Activity

  1. added
    bugSomething isn't working as documented
    on Dec 16, 2024
  2. lgrammel commented on Dec 16, 2024

    @lgrammel
    Collaborator

    The error will happen after the function in your example has returned the stream text result, and the return type is only a Promise bc your method is async (it does not need to be in your example).

    Therefore your try block can never catch it. The error happens during the consumption of the stream, not during the creation of the stream text result.

    I would need to know more about how you use the result / consume the stream.

  3. samihamine commented on Dec 17, 2024

    @samihamine
    Author

    Thanks @lgrammel for pointing out that the issue occurs after the function has returned the stream text result. This clarification helped me resolve the problem. I truly appreciate your quick response and detailed explanation – it was incredibly helpful.

    Thanks again for your support!

  4. kachar commented on Dec 20, 2024

    @kachar

    This was useful:

    import { generateText } from 'ai';
    
    try {
      const { fullStream } = streamText({
        model: yourModel,
        prompt: 'Write a vegetarian lasagna recipe for 4 people.',
      });
    
      for await (const part of fullStream) {
        switch (part.type) {
          // ... handle other part types
    
          case 'error': {
            const error = part.error;
            // handle error
            break;
          }
        }
      }
    } catch (error) {
      // handle error
    }

    Straight out of the docs: https://sdk.vercel.ai/docs/ai-sdk-core/error-handling#handling-streaming-errors-streaming-with-error-support

  5. Pascaltib commented on Jan 23, 2025

    @Pascaltib

    I don't really understand the streaming process but how would this work when you return the stream as a response with toDataStreamResponse()?

    try {
    
    	const result = streamText({
    		model: aiSdkOpenAI(GPT_VERSION),
    		messages: reqMessages
    	})
    
    	return result.toDataStreamResponse()
    } catch (err) {
    	// TODO: THIS DOES NOT WORK
    	console.error(err)
    	throw err
    }

    In this example, how can I capture any errors?

    If I add the example in the documentation it will work and will log the error but the text will no longer be streamed.

    try {
    
    	const result = streamText({
    		model: aiSdkOpenAI(GPT_VERSION),
    		messages: reqMessages
    	})
    
            for await (const part of result.fullStream) {
    		switch (part.type) {
    			// ... handle other part types
    
    			case 'error': {
    				const error = part.error
                                     // This works
    				console.error(error)
    				break
    			}
    		}
    	}
    
    	return result.toDataStreamResponse()
    } catch (err) {
    	// TODO: THIS DOES NOT WORK
    	console.error(err)
    	throw err
    }
  6. lgrammel commented on Feb 6, 2025

    @lgrammel
    Collaborator

    I have introduced an onError callback on streamText: #4729 ([email protected])

    streamText immediately starts streaming to enable sending data without waiting for the model.
    Errors become part of the stream and are not thrown to prevent e.g. servers from crashing.

    To log errors, you can provide an onError callback that is triggered when an error occurs.

    import { streamText } from 'ai';
    
    const result = streamText({
      model: yourModel,
      prompt: 'Invent a new holiday and describe its traditions.',
      onError({ error }) {
        console.error(error); // your error logging logic here
      },
    });
  7. Pascaltib commented on Feb 6, 2025

    @Pascaltib

    Ahh thanks Igrammel!

    That's amazing I will be using that for sure :)

    Thanks for the help

  8. polly3d commented on Mar 1, 2025

    @polly3d

    I have introduced an onError callback on streamText: #4729 ([email protected])

    streamText immediately starts streaming to enable sending data without waiting for the model. Errors become part of the stream and are not thrown to prevent e.g. servers from crashing.

    To log errors, you can provide an onError callback that is triggered when an error occurs.

    import { streamText } from 'ai';

    const result = streamText({
    model: yourModel,
    prompt: 'Invent a new holiday and describe its traditions.',
    onError({ error }) {
    console.error(error); // your error logging logic here
    },
    });

    That's save my day. Thanks so much!

  9. lucasbaze commented on Mar 12, 2025

    @lucasbaze

    Ran into the same issue. The newest update [email protected] solves this.

  10. rob-gordon commented on Mar 22, 2025

    @rob-gordon

    Even with the onError callback I was still struggling to get the error message to the client side because I had already returned result.toDataStreamResponse() from my route.

    I was only getting the cryptic "An error occured" response in the stream. For anyone else struggling with this I was able to solve it with toDataStreamResponse's getErrorMessage property.

    let errorMessage = "";
    const result = streamText({
      model: getModel(),
      messages,
      onError: ({ error }) => {
        errorMessage = error.message; // <-- this is simplified, had to do a bit of parsing to get the real message
      },
    });
    
    // Return text stream response with proper headers
    return result.toDataStreamResponse({
      getErrorMessage(error) {
        return errorMessage;
      },
    });
  11. lgrammel commented on Mar 24, 2025

    @lgrammel
    Collaborator

    @rob-gordon the error that you received in onError and getErrorMessage should be the same error. If you only want to forward the error message to the client, you do not need onError, you can just extract the error msg in getErrorMessage, see also https://sdk.vercel.ai/docs/ai-sdk-ui/chatbot#error-messages

  12. hendychua commented on Apr 7, 2025

    @hendychua

    Thanks @lgrammel for your replies on this thread, which have been helpful for me.

    I am implementing fallbacks with streamText. Basically I have an array of models to try and I am iterating through the models with streamText being a call in each iteration. I followed the suggestion here - #4099 (comment) - to catch the errors synchronously (and if any errors are present move to the next iteration).

    I’m also using dataStreamWriter to merge the resp from streamText.

    The issue I’m facing now is that even though the error from the first model is caught and the fallback model is being used, the error is still being streamed back to the client immediately. (I’m using useChat on the client.) the client receives an error part (https://sdk.vercel.ai/docs/ai-sdk-ui/stream-protocol#error-part) and displays an error message despite valid data being streamed back after that.

    Do you have any suggestions on how to work around this or if I’m doing something incorrectly?

    Thank you!

  13. lgrammel commented on Jul 22, 2026

    @lgrammel
    Collaborator

    Identify issue type

    Identified as Feature
    Confidence: High

    Rationale

    The reported behavior was clarified as expected streaming semantics rather than a defect. The resulting product change was an API/ergonomics enhancement for observing streaming errors, making this a feature.

    Evidence

    • Maintainer clarified that streaming errors occur after streamText returns and must be handled while consuming the stream, so the shown synchronous try/catch cannot catch them.
    • The reporter confirmed that this explanation resolved the original problem.
    • A later use case prompted the addition of a new streamText.onError callback in [email protected] via PR feat (ai/core): add onError callback to streamText #4729.
  14. added theissue type on Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions