Repository navigation
generateText(): generated text is empty when used with custom tool #6414
Description
Activity
Hey, trying again, is this really the way to solve this issue?
hi can you log any potential errors with onError? also what is returning an empty result, your tool or the text generation?
Hey, I'll check the onError and get back to you.
The tool returns responses correctly, but the text generation returns empty unless I block the tools from hogging all the allocated requests (like you can see in the shared code)I'm not sure where can I put the onError, I don't see it as an option in generateText, what am I doing wrong?
Nevermind, was thinking this is stream text. So you are seeing no errors?
No errors, toolsResult return the tool results, text result is empty and experimental_output crashes
Reacted by Luis Junior, Gaurav Bhandari and Felipe Torres (fforres)- addedbugSomething isn't working as documentedSomething isn't working as documentedai/corecore functions like generateText, streamText, etc. Provider utils, and provider spec.core functions like generateText, streamText, etc. Provider utils, and provider spec.and removed
on Jun 26, 2025 same issue here after I upgrade to v5. the result.content is tool call info. and result.text is empty
Reacted by Luis Junior, Aman Thakur, ntamotsu, Gaurav Bhandari, Neil Poulin, Narix Hine, Ethan Cohen and Felipe Torres (fforres)Same issue here after I upgrade to v5. Still no fix?
Actually the code stays about the same... not a lot have changed in those part specifically (except for tool -> parameters to inputSchema):
import { tool, generateText, Output } from 'ai'; import dotenv from 'dotenv'; import { openai } from '@ai-sdk/openai'; import { z } from 'zod'; dotenv.config(); const exampleTool = tool({ description: ' "Use this tool to fetch real-time web data for queries that require the latest information, niche topics, local context, or when the answer may have changed since the model’s last update. This includes news, recent events, evolving technologies, up-to-date documentation, or anything likely not captured in training data. The tool supports issuing queries to a search engine and browsing the returned links to extract relevant content. ALWAYS PASS engine parameter with \'google\'', inputSchema: z.object({ query: z.string() }), // Function to execute the search execute: async ({ query }) => { const results = [{ "url": "https://www.google.com", "snippet": "Google is a search engine" }, { "url": "https://www.yahoo.com", "snippet": "Yahoo is a search engine" }] // Return in the format our app expects return JSON.stringify({ results }); } }) const searchResultsSchema = z.object({ results: z.array(z.object({ url: z.string(), snippet: z.string() })) }); const prompt = `Find me a site that has a blog post about the latest news in the world`; const response = await generateText({ model: openai.responses('gpt-4o-mini'), prompt: `${prompt} IMPORTANT: You must ALWAYS return this exact structure no matter what the tool returns. NEVER return raw markdown or the original tool response or empty strings.`, tools: { exampleTool }, tool_choice: { type: 'tool', toolName: 'exampleTool' }, experimental_output: Output.object({ schema: searchResultsSchema }), debug: true, onStepFinish({ text, toolCalls, toolResults, finishReason, usage }) { // Log the tool results } }); // Safely access results, handling null/undefined let results = response.experimental_output?.results || []; console.log(results);
use the stepCountIs. I had the same issue and it was fixed adding
const result = await generateText({ model: openai("gpt-5"), stopWhen: stepCountIs(2), });
const { generateText, tool, stepCountIs } = await import("ai");This works, but I don't understand why... why do I have to add stopWhen property and why isn't that documented?
Wow, this is really weird. I had the same issue and why the heck is
stopWhen: stepCountIs(2)solving this?edit: do not forget to import stepCountIs from ai:
import { generateText, stepCountIs } from 'ai';- addedai/providerrelated to a provider package. Must be assigned together with at least one `provider/*` labelrelated to a provider package. Must be assigned together with at least one `provider/*` labelprovider/openaiIssues related to the @ai-sdk/openai providerIssues related to the @ai-sdk/openai provider
on Oct 15, 2025 So the reason you need to add the
stopWhenparameter is that the generateText and other primitive functions only execute in one step (i.e. one call)See docs ref here
By adding that parameter, you essentially let the api be called twice, now with the toolResult. Which then results in obtaining final text.
Since this is documented and expected behaviour, marking this as closed
Identify issue type
Identified as None
Confidence: HighRationale
This is primarily a usage/support question resolved by configuring documented multi-step execution, not a confirmed bug or a request to change the product or documentation.
Evidence
- The request forces a tool call, while
generateTextperforms only one step by default, leaving no subsequent step to generate final text from the tool result. - Adding
stopWhen: stepCountIs(2)enables the required second model call and resolves the behavior. - The closing comment identifies this as documented, expected multi-step tool-calling behavior rather than an SDK failure.
- The request forces a tool call, while
Description
Hi, I have a custom tool I wrote and when I'm trying to use it with generateText it return an empty text result.
my tool definition (assume I see the code is called and return result)
And this is my call:
Adding the following parts work:
Is there a better way to control how many times a tool is called? what am I doing wrong?
AI SDK Version