Tools Not Working After First Turn When Using previous_response_id

Issue Description

I’m experiencing an issue with the Responses API where tool calling stops working after the first iteration when using previous_response_id for multi-turn conversations.

( I am trying to save some bandwidth/ token when tool descripitons are in large contents)

Current Behavior

  • First iteration: Tools are called successfully

  • Subsequent iterations: Tools are never invoked, even when the model’s response suggests it should use them.

My Implementation

Here’s my simplified code structure:

go

for i := range maxIterations {
    params := ResponseNewParams{
        Model: deploymentName,
        Input: inputItems,
        Store: true,
    }

    // Only passing tools in first iteration
    if i == 0 {
        params.Tools = tools
    }

    if previousResponseID != "" {
        params.PreviousResponseID = previousResponseID
    }

    // Make streaming request
    stream := client.Responses.NewStreaming(ctx, params)
    response := processStream(stream)
    
    previousResponseID = response.ID
    
    // Execute any tool calls and prepare next iteration
    inputItems = executeToolCalls(response)
}

Question

Does the Responses API require tools to be passed in every request, even when using previous_response_id?

I initially assumed that tools configuration would be inherited from the previous response (similar to how conversation context is preserved), but my testing suggests otherwise.

Yes, you’ve made a correct conclusion. “tools” parameter, the specification of which tools are available to the AI model, are not part of a conversation maintained by the server.

You will need to pass your list of tools and functions in each API call, unless using a Responses API “prompt” ID that contains a fixed set of tools at the time of creation, and is equivalent to sending the same tools list yourself.

The specification becomes part of the initial system message that OpenAI runs: a listing of function tools and internal tools described to the AI.

There is reasoning for them to be dynamic - you might not want to offer “schedule_appointment” until you’ve seen that “check_available_appointments” has been used.

“instructions” is also a parameter that must be sent every time, which inserts a developer message before any prior conversation state or any input that you send.

A “conversation state” can be maintained by use of a previous_response_id chain, by the “conversations” API endpoint (where you create an ID to pass to the calls to maintain and grow a chat thread), or your own complete management of the input each turn.

3 Likes