Infinite loop [Run]: requires_action -> processing -> requires_action

We encountered a random case with the Assistant api, where Run object was returning a status of "requires_action" and after submitting the tool output successfully it would return the status of "processing" for a few seconds then again "requires_action" with the exact same arguments.

After run.create, loop:

  • Run status = processing
  • Run status = requires_action
  • Submit tool output (success)
  • Run status = processing
  • Repeats loop

Our processing code runs in a loop checking the Run status:

#class Assistant:
#...
    def process(self, *, message=None):
        """
        Processes the incoming message by creating a message in the thread, initiating a run, and handling the run's lifecycle.
        This method takes a message, sends it to the thread, and manages the run created for this message. It handles different
        run statuses. For runs requiring action, it processes necessary tool outputs. The method waits for the run to complete 
        and retrieves the final message from the thread.
        Args:
            message (str): The message to be processed.
        Returns:
            str: The content of the final message from the completed run.
        """
        if not message:
            raise ValueError("Message is required")
        try:
            thread = self.thread
            print(f"[DEBUG][Assistant.process] Thread ID: {thread.id}")
            new_message = client.beta.threads.messages.create(
                thread_id=thread.id,
                role="user",
                content=[{ 'text': message, "type": 'text' }],
            )
            print(f"[DEBUG][Assistant.process] Created message with ID: {new_message.id} for Thread ID: {thread.id}")
        except Exception as e:
            print(f"[ERROR][Assistant.process] Failed to create message or run: {e}")
            raise
        run = client.beta.threads.runs.create(
            thread_id=thread.id,
            assistant_id=self.Assistant_id,
        )
        print(f"[DEBUG][Assistant.process] Created run with ID: {run.id} for Thread ID: {thread.id}") # type: ignore - SEE: https://platform.openai.com/docs/api-reference/runs/getRun
        # The status of the run can be one of: queued, in_progress, requires_action, cancelling, cancelled, failed, completed, incomplete, or expired
        while run.status in ["in_progress", "queued", "requires_action"]:
            try:
                print(f"[DEBUG][Assistant.process] Latest run status: {run.status}")
                if run.status == "requires_action":
                    print(f"[DEBUG][Assistant.process] Run status is requires_action")
                    # if run.required_action.type == "submit_tool_outputs":
                    tool_call_outputs = handle_required_action(
                        run, 
                        message, 
                        thread_id=thread.id, 
                        message_id=new_message.id
                    )
                    print(f"[DEBUG][Assistant.process] Complete tool call outputs: {tool_call_outputs}")
                    if tool_call_outputs and len(tool_call_outputs) > 0:
                        client.beta.threads.runs.submit_tool_outputs(
                            thread_id=thread.id,
                            run_id=run.id,
                            tool_outputs=tool_call_outputs
                        )
                        print(f"[DEBUG][Assistant.process] Submitted tool outputs to Run ID: {run.id}")
                    else:
                        print(f"[ERROR][Assistant.process] No tool call outputs!!")
                print(f"[DEBUG][Assistant.process] ===== Waiting for next poll =====")
                time.sleep(1)  # Sleep to avoid hitting the API rate limit
                run = client.beta.threads.runs.retrieve(run_id=run.id, thread_id=thread.id)
            except Exception as e:
                print(f"[ERROR][Assistant.process] Error during message retrieval or processing: {e}")
                raise
        if run.status == "completed":
            messages = client.beta.threads.messages.list(thread_id=thread.id, run_id=run.id)
            message = {
                'role': messages.data[-1].role,
                'content': messages.data[-1].content[0].text.value,
                'id': messages.data[-1].id
            }
            return message
        elif run.status == "failed":
            raise ValueError(f"Run status is failed ({run.status}): {run.last_error}")
        else:
            raise ValueError(f"Run status is not completed ({run.status}): {run.last_error}")

This issue caused a large spike in the OpenAI api bill just from that single Run.

Any ideas how to fix this?
It has only happened once so far from hundreds of Runs.

1 Like

I got the exact same situation happening with my assistant app, and I have not found any solutions. It happens every time for a specific input message. If i run the assistant the exact same way with any other message it doesn’t seem to happen.

This started happening to me when i added the json_schema to the assistant. ):

Found any solution ?, because Facing the same issue whenever I add json_schema to response_format

Anyone found any fix / updates on this. Been getting this issue since started using json_schema as well.

I encountered the same issue with streaming function calls, but in my case, it occurs with (so far) every run, not just randomly.

The until_done stream function is supposed to wait for the stream to finish, which I assume should happen once a run is complete and the status changes to "completed". However, the run status still shows as "requires_action", which causes a BadRequestError when attempting to start a new run in the same thread.

My current workaround (though not the best coding practice) is to cancel the run after retrieving the answer and once the stream has ended. I do this by parsing the run ID and canceling the run manually, like so:

openai.OpenAI.beta.threads.runs.cancel(
    thread_id=[thread_id],
    run_id=[run_id],
)

Reference: OpenAI API Docs - Cancel a Run

This will result in the run status being marked as "cancelled", which doesn’t signify completion but effectively ends the run. This could serve as a temporary fix for you as well—manually closing the run after the final answer is retrieved.

My guess is that the function calls expect a response in cases where additional information is needed (such as with get_weather, where weather information still needs to be added to the message). Therefore, the run cannot be completed automatically.

For context, I am using:

  • Assistant API v2
  • Function calls with JSON Format
  • ChatGPT-4o-mini
  • OpenAI Python SDK Version 1.9.0