Gemini Interactions API Migration Guide: How to troubleshoot after changing outputs to steps

The old outputs schema of the Gemini Interactions API has been removed. This article explains how to use steps, output_text, new SSE events, tool calls, and session history instead, and quickly locate common errors after migration.

The Gemini Interactions API has become the recommended unified interface for new projects. It represents a generation, tool call, thinking process, tool result and final reply as observable execution steps, suitable for Agents, long tasks and applications that need to display intermediate states.

This also means that legacy code can no longer assume outputs in the response. Starting in May 2026, the new schema will become the default; the old schema was removed on June 8. If there are empty replies, tool calls are lost, SSE events are not triggered, or historical context is broken after migration, it is usually not a model problem, but that the data is still being read according to the old structure.

First confirm which layer you are migrating to

Before troubleshooting, categorize the problem first to avoid changing just one field:

Hierarchy Old way New way
Final text Traverse outputs to find text Simple scene reading interaction.output_text
Full response Find content, tools, or search results in outputs Traverse interaction.steps, process as type
Tool call Parse from old content type Process steps such as function_call, function_result
Streaming events content.start, content.delta, content.stop step.start, step.delta, step.stop
Multi-round history Return old outputs Use previous_interaction_id on the server, or return steps on the client

If the SDK is still Python 1.x or JavaScript 1.x, confirm the version first. The official migration instructions require using Python ≥2.0.0 or JavaScript ≥2.0.0 to automatically adopt the new schema; but upgrading the SDK will not automatically fix the outputs parser, SSE processor and front-end state machine you wrote yourself.

Easiest fix: read the final text with output_text

When processing only plain text questions and answers, there is no need to manually find the last text from steps. The new version of the SDK provides output_text, which extracts and splices consecutive text blocks at the end of the response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from google import genai

client = genai.Client()

interaction = client.interactions.create(
    model="gemini-3.5-flash",
    input="用三句话解释什么是 prompt caching。",
)

print(interaction.output_text)

If you leave interaction.outputs[-1].text as is after migration, common results are that the property does not exist, indexing fails, or the object is incorrectly serialized into an empty string. First replace it with interaction.output_text, and then add the step analysis of complex scenarios.

output_text is not suitable for all responses: when text is separated by thoughts, images, audio, or tool calls, it won’t restore all interleaving content for you. When you need to show the complete execution process, you should traverse steps.

How to properly traverse steps

The new schema represents processes as typed steps. At a minimum, user_input, model_output, function_call, and function_result will be recognized; if using server-side tools, you will also see google_search_call, google_search_result, or other tool-specific steps.

The following example only collects text from the final model output:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def collect_model_text(interaction):
    chunks = []

    for step in interaction.steps:
        if step.type != "model_output":
            continue

        for item in step.content or []:
            if item.type == "text":
                chunks.append(item.text)

    return "".join(chunks)


print(collect_model_text(interaction))

In real projects, don’t assume that content exists for every step, and don’t treat thought as the final answer. First branch according to step.type, and then read the fields allowed by this type. Record unknown steps as type, ID, and status to preserve forward compatibility; don’t let the entire request fail just because a new tool step appears.

Why does the tool call “disappear”?

Older schemas often wrap server-side tool operations in a specific content type called outputs. The new schema breaks these into independent steps, so scanning only model_output will not see the search, function calls, and function results.

For example, the function call would read like this:

1
2
3
4
5
6
for (const step of interaction.steps) {
  if (step.type === 'function_call') {
    console.log(`Calling ${step.name}`);
    console.log(step.arguments);
  }
}

Server-side Google Search should also handle the call and result steps separately, rather than guessing from the final text whether a search was performed or not. For client function tools, after receiving requires_action, submit the corresponding function_result and continue to use the status of the interaction to complete the subsequent process.

Streaming SSE events are migrated together

If you only change the REST response parsing and do not change the stream processor, the front end will usually behave as “the request is successful but the page is not updated.” The new event names are as follows:

Old events New events
interaction.start interaction.created
interaction.complete interaction.completed
content.start step.start
content.delta step.delta
content.stop step.stop
interaction.status_update interaction.in_progress, interaction.requires_action and other status events

The parameters of the function call are no longer guaranteed to arrive completely at once. In the stream, step.start will first give the function name, and then multiple step.delta events will be delivered piece by piece with some JSON parameters. The client must accumulate parameters and parse the complete JSON after step.stop or a state requiring action; do not directly JSON.parse() for each delta.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let rawArguments = '';

for await (const event of stream) {
  if (event.event_type === 'step.delta' && event.delta?.type === 'arguments') {
    rawArguments += event.delta.partial_arguments;
  }

  if (event.event_type === 'step.stop') {
    // 只在对应 function_call 步骤完成后解析 rawArguments。
  }
}

Do not reply after multiple rounds of dialogue outputs

The old stateless implementation often collects the previous round of outputs and stuffs it back into the next round of input. There are two clearer options after migration:

  • Continue the same session via previous_interaction_id using the default server-side state;
  • When you must manage the history by yourself, return the previous round of steps and add a new user_input step.

Server-side status is more suitable for ordinary multi-round conversations; client-side status is used when strict control of saved content, cross-system migration, or offline playback is required. No matter which one you choose, do not splice the old outputs and the new steps at the same time, otherwise it is easy to produce duplicate contexts, tool result mismatches, or token proliferation.

response_format may also be the source of the error

This migration not only changes the output array. Structured output and multi-modal configuration are also unified into the top-level response_format:

  • The original response_mime_type has been removed; specify mime_type in the response_format entry.
  • The JSON schema needs to be put in a format object with type: "text".
  • Picture configuration moved from generation_config to response_format entry in type: "image".
  • Multimodal output requires an array of format entries.

If the interface returns 400, in addition to checking for steps, the code is also searched for response_mime_type, generation_config.image_config, and the legacy single object response_format. These old fields often appear together with the outputs problem after the same upgrade.

A practical troubleshooting procedure

  1. Upgrade the Google GenAI SDK and document the actual version.
  2. Verify interaction.output_text with a toolless, non-streaming minimal request.
  3. Print the type and status of interaction.steps to confirm that the program has received the new schema.
  4. Migrate the parsing logic of function calls, server-side searches, and tool results respectively.
  5. Modify SSE event subscription and parameter accumulation logic.
  6. Check if the multi-round history is changed to previous_interaction_id or steps.
  7. Search and replace old response_format related fields.
  8. Run regression tests one each with text, tool calls, streaming output, and failure retries.

Don’t rely on the old Api-Revision header to temporarily restore old code. The transition period for the old schema is over; migration should now be considered an official compatibility fix, rather than continuing to have two sets of inconsistent parsers.

Summary

The essence of changing outputs to steps is that the Interactions API no longer crams all processes into an output list, but explicitly exposes inputs, reflections, tools, results, and final outputs. Pure text scenes preferentially use interaction.output_text; Agent, tool, streaming and multi-modal scenes use step.type to establish explicit branches; multi-round sessions use previous_interaction_id or steps instead. By migrating according to these three principles, most of the “no output”, “tool missing” and “SSE does not refresh” problems can be located.

Official information:

记录并分享
Built with Hugo
Theme Stack designed by Jimmy