All Background Tasks on Responses API producing completely empty output array across all prompts

I have multiple prompts in a project, across all models, that hasn’t had any changes that is using both reasoning and output tokens, status is completed, etc. with a completely empty output array.

I am sending calls to the Responses API as background tasks, and notifying an app on completion with an outgoing webhook. All of that works okay and I’m still getting fully empty response arrays.

  • Have tried multiple models
  • Have tried bare minimum input
  • Prompt works in the Platform UI but not over API, empty response is returned

ANY call to this prompt over API instead of in the UI is producing an empty output array. Anyone else experiencing any problems like this?

More information: I have narrowed this down to background tasks. All background tasks to any prompt in this project fails to provide any output, but the API is recording token usage.

#returns output

curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
  "prompt": {
    "id": "pmpt_ID",
    "version": "Version",
    "variables": {
        "variablesHere": "value"
        ...
    }
  }
}' 
#does not return any output
#output is []
#usage shows thousands of tokens being used

curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
  "prompt": {
    "id": "pmpt_ID",
    "version": "Version",
    "variables": {
        "variablesHere": "value"
        ...
    }
  },
  "background": true
}' 
4 Likes

Any way to get support from @OpenAI_Support on this? This is extremely disruptive to my production environments and I find it difficult to believe no one else is having similar issues. Thanks!

1 Like

You will have to be more specific about what you are doing.

Model, schema, etc.

Perhaps you are not iterating through the output list to actually find content, not skipping reasoning or encrypted content, or not checking the status for correct types, or not requesting enough output tokens on a reasoning model?


I typed up a procedural demo with what you furnished as info, an “array” schema and a background request. You can walk through every step, or try your payload. See if it is your code or the input being sent.
This also doesn’t go into polling if the initial response is done (disabled background).

import os, sys, json, time, logging, httpx

logger = logging.getLogger(__name__)

def get_api_key_headers():
    return {"Authorization": f"Bearer {os.environ.get("OPENAI_API_KEY")}"}

def poll_response(response_id: str, timeout: float = 1200.0, interval=1.0, printing=True, delay=4
                  ) -> httpx.Response | None:
    """
    Poll GET /v1/responses/{response_id} every 2 seconds until it leaves
    'queued'/'in_progress', or until `timeout` seconds elapse.

    Returns the final httpx.Response on success; logs a warning and returns None on
    timeout or HTTP/client errors.

    status: One of [completed, failed, in_progress, cancelled, queued, incomplete]
    """
    if not response_id:
        raise ValueError("No response ID received by poll_response")
    url = f"https://api.openai.com/v1/responses/{response_id}"
    deadline = time.monotonic() + timeout
    response = None
    time.sleep(delay)
    try:
        with httpx.Client(timeout=20) as client:
            while True:
                time.sleep(interval)
                interval=interval + 0.1  # don't keep hitting every second at the 20 minute mark...
                response = client.get(url, headers=get_api_key_headers())
                response.raise_for_status()

                data = response.json()
                status = data.get("status")

                if printing:
                    print(f"poll status: {status}...")
                if status not in ("queued", "in_progress"):
                    if status in ("failed", "cancelled"):
                        raise RuntimeError(f"Poll failed {response_id}:\n {status}")
                    logger.info("Response ID reached terminal state: %s (%s)", response_id, status)
                    return response
                if time.monotonic() >= deadline:
                    raise TimeoutError(f"Poll {timeout:.3f}s timeout {response_id}:\n {status}")
    except Exception as exc:
        try:
            msg = str(response.status_code) + ": " \
                  + json.loads(response.content.decode())["error"]["message"]
        except Exception:
            msg = str(exc)
        logger.warning("Couldn't poll %s:\n %s", response_id, msg)

def delete_response(response_id: str) -> None:
    if response_id:
        try:
            with httpx.Client(timeout=20) as client:
                response = client.delete(
                    f"https://api.openai.com/v1/responses/{response_id}",
                    headers=get_api_key_headers(),
                )
                response.raise_for_status()
                logger.info("Response ID deleted: %s", response_id)
        except Exception as exc:
            msg=str(response.status_code) + ": " \
                + json.loads(response.content.decode())["error"]["message"]
            logger.warning(f"Couldn’t delete %s:\n %s",
                           response_id, msg)
            pass

## ======== a schema string (like a paste from the playground JSON ==========
schema_format = r"""
{
  "schema": {
    "type": "object",
    "properties": {
      "answers": {
        "type": "array",
        "description": "List of answer strings.",
        "items": {
          "type": "string",
          "description": "An answer string."
        }
      }
    },
    "required": [
      "answers"
    ],
    "additionalProperties": false
  },
  "strict": true
}
""".strip()

## ======== a schema container ==========
text_param = {
    "format": {
        "type": "json_schema",
        "name": "json_response",
        "description": "Ordered lists in JSON sent to API; max items = 100",
        **json.loads(schema_format)
    },
    "verbosity": None
}

## ======== API call construction ==========
model = "gpt-5-mini"
payload = {
    "model": model,
    "instructions": ("You are a non-conversational AI that produces JSON arrays of items sent to an API."
                     "\nAutomatically fulfill natural-language queries requesting any generated lists."),
    "input": "List twenty dog breeds, ranked descending by US popularity.",
    "background": True,
    # "store": False,  # background mode must "store"
    "max_output_tokens": 2000,
    "text": {"format": {
        "type": "json_schema",
        "name": "json_response",
        "description": "Ordered lists in JSON sent to API; max items = 100",
        **json.loads(schema_format)
    }},
}
print("\n---\n\nRequest JSON:\n" + json.dumps(payload, indent=2))   

## ======== API call - create ==========
try:
    response = httpx.post(
        "https://api.openai.com/v1/responses",
        json=payload,
        headers={**get_api_key_headers()},
        timeout=600,  # set lower if doing background only
    )
    response.raise_for_status()
    response_headers = {k: v for k, v in response.headers.items() if v.startswith('x-')}
    response_id = response.json().get("id")
    response_status = response.json().get("status")
    print(f"\n---\n\n{model} Initial Response JSON:\n" + json.dumps(response.json(), indent=2))

    ### - Expected parsing from initial non-background response
    assistant_texts = [
        content["text"]
        for output in response.json().get("output", [])
        for content in output.get("content", [])
        if content.get("type") == "output_text" and "text" in content
    ]
    print("\n---\n\nInitial response text:\n" + str(assistant_texts))
except:
    print(
        response.status_code,
        json.loads(response.content.decode())["error"]["message"]
    )
    raise

if response_status in ("queued", "in_progress"):
    response = None; assistant_texts = None
    ## ======== API call - poll if initial is not terminal, return final ==========
    response = poll_response(response_id, timeout=60.0, interval=0)
elif response_status in ("failed", "cancelled"):
    raise RuntimeError(f"Inicial call failed failed {response_id}:\n {response_status}")
else:
    print(f"Was this status incomplete?: {response_status}")

delete_response(response_id)
if response and response.json():
    print(f"\n---\n\n{model} Final Response JSON:\n" + json.dumps(response.json(), indent=2))
    assistant_texts = [
        content["text"]
        for output in response.json().get("output", [])
        for content in output.get("content", [])
        if content.get("type") == "output_text" and "text" in content
    ]
    print("\n---\n\nCollected response text:\n" + str(assistant_texts))

poll status: queued…
poll status: queued…
poll status: completed…
Collected response text:
[‘{“answers”:[“1. Labrador Retriever”,“2. French Bulldog”,“3. Golden Retriever”,“4. German Shepherd Dog”,“5. Poodle”,“6. Bulldog”,“7. Beagle”,“8. Rottweiler”,“9. German Shorthaired Pointer”,“10. Dachshund”,“11. Pembroke Welsh Corgi”,“12. Siberian Husky”,“13. Australian Shepherd”,“14. Chihuahua”,“15. Shih Tzu”,“16. Boston Terrier”,“17. Bernese Mountain Dog”,“18. Pomeranian”,“19. Havanese”,“20. Shetland Sheepdog”]}’]

2 Likes

Thanks for taking the time to reply to this. I should have been more explicit on the set up and issue. I’ll express everything here in pseudocode for the sake of time.

I have an OpenAI Project with multiple prompts in it. Due to the nature of the use case and work being done, calls to the Responses API effectively must be in background mode. All calls were “working” (as in returning output, in expected format, etc.) prior to September 12, and 100% of calls after September 12 are now not working. No changes have been made to the client side, or the OpenAI side/the project. So hundreds of calls to Responses API prior to September 12 were working fine.

I have replicated this on gpt-5, gpt-4, and o3

I have an outgoing webhook from OpenAI notifying on all events related to the Responses API.

Here’s an example flow

POST /responses

curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
  "prompt": {
    "id": "pmpt_ID",
    "version": "Version",
    "variables": {
        "variablesHere": "value"
        ...
    }
  },
  "background": true
}'

From this, 200 OK received

{
  "id": "resp_ID",
  "object": "response",
  "created_at": time,
  "status": "queued",
  "background": true,
  ...
}

Model completes > Webhook fires

{
  "id": "evt_ID",
  "object": "event",
  "created_at": time,
  "type": "response.completed",
  "data": {
    "id": "resp_ID"
  }
}

I respond to this event with

GET /responses/resp_ID

{
  "id": "resp_ID",
  "object": "response",
  "created_at": time,
  "status": "completed",
  "background": true,
  "error": null,
  "incomplete_details": null,
  "instructions": [
   ...
   ]
  "model": "gpt-5-2025-08-07",
  "output": [],
  ...
   "usage": {
    "input_tokens": 6674,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 3292,
    "output_tokens_details": {
      "reasoning_tokens": 2496
    },
    "total_tokens": 9966
  }
}

So in this flow, the model(s) are generating output/using tokens but not appending output to the rebturned data/output key.

For all prompts in this project, when Background Mode is enabled, output is an empty array:

"output": []

But usage indicates clear content generation. Prior to September 12, output contained expected output and content from the prompt. This is now happening across all models that I have tried without any change on the OpenAI side (from me) or client side.

Taking the webhook out of the equation, if I just poll these responses as you have in an isolated environment, I’m still getting “empty” output.

If I take Background Mode out of the equation:

curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
  "prompt": {
    "id": "pmpt_ID",
    "version": "Version",
    "variables": {
        "variablesHere": "value"
        ...
    }
  }
}

This this returns a response that does have data within the output key. The only delta here is background mode being on or off.

1 Like

Here’s what I would do:

Observer the platform site logs for these request IDs.

See if you have a response there.

Pull down the request ID manually, observe the shape of the output list object, the status, the finish reason..

Regenerate a new prompt ID version - or just don’t use a prompt ID to make calls.
Force a high max_output_tokens on the call itself, as this is not part of “prompts” that is stored.

Drop the schema, and use text type:text and see if it the model that was going nuts on the JSON format (as responses + mini models = bad time). That your instruction prompting alone is enough to still deliver JSON in the shape.

Inspect the event from webhook closely, and filter noise or status events that are not “completed”

ETC. Persistence will reveal how you are spending tokens but not ingesting a final response.

2 Likes

This has been solved.

Resolution: ZDR controls were not correctly blocking Background Mode on this org. What I am/was experiencing is actually expected behavior, it should not have been working previously but was.

1 Like

Thank you for the update with the cause and the solution.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.