Skip to content

[Bug]: openai-codex provider fails with 'Responses API returned no output items' when output[] is empty but streaming delivered text (gpt-5.4) #5678

@kht2199

Description

@kht2199

Bug Description

When using the openai-codex provider with gpt-5.4, Hermes fails with RuntimeError: Responses API returned no output items even though the streaming API delivers text via output_text.delta events. The final response.output list is empty, but the model IS responding — the text arrives through streaming events.

This causes:

  1. All 3 retries to fail with the same error
  2. Each retry's streaming text being concatenated and shown as a single garbled response (e.g. hellohellohello)

Steps to Reproduce

  1. Configure openai-codex provider with gpt-5.4 via OAuth
  2. Run hermes chat -q "say hello"
  3. Observe 3 retries each with Invalid API response: response.output is empty

Expected Behavior

Hermes should use the text received via streaming output_text.delta events as the response content when response.output is empty but status == 'completed'.

Actual Behavior

⚠️  Invalid API response (attempt 1/3): response.output is empty
⚠️  Invalid API response (attempt 2/3): response.output is empty  
⚠️  Invalid API response (attempt 3/3): response.output is empty
❌ Max retries (3) exceeded for invalid responses. Giving up.
hellohellohello   ← concatenated text from all 3 retry streaming events
Error: Invalid API response shape. Likely rate limited or malformed provider response.

Root Cause Analysis

The Codex API (via https://chatgpt.com/backend-api/codex) returns streaming output_text.delta events with text, but the final response.output list is empty ([]). The response has status='completed' and non-zero usage.output_tokens.

Two places raise errors:

  1. _normalize_codex_response (run_agent.py ~line 3447):
if not isinstance(output, list) or not output:
    raise RuntimeError("Responses API returned no output items")
  1. Validation code (~line 7368):
elif len(output_items) == 0:
    response_invalid = True
    error_details.append("response.output is empty")

Both assume output[] will always contain the response, but the Codex API now delivers text via streaming events only, leaving output[] empty.

Proposed Fix

In _run_codex_stream(), accumulate output_text.delta events and store as self._last_stream_accumulated_text. Then in _normalize_codex_response(), use this as a fallback to synthesize a message item when output[] is empty:

# _normalize_codex_response
if not isinstance(output, list) or not output:
    accumulated = getattr(self, "_last_stream_accumulated_text", None)
    if accumulated:
        synthetic_item = SimpleNamespace(
            type="message",
            content=[SimpleNamespace(type="output_text", text=accumulated)],
            role="assistant",
        )
        output = [synthetic_item]
    else:
        raise RuntimeError("Responses API returned no output items")

Affected Component

  • Gateway (Telegram/Discord/Slack/WhatsApp)
  • Agent Core (conversation loop, context compression, memory)

Messaging Platform

  • Discord
  • Telegram

Operating System

macOS 25.4.0 (Darwin)

Python Version

3.11.15

Hermes Version

v0.7.0 (2026.4.3)

Relevant Logs

WARNING root: Invalid API response (retry 1/3): response.output is empty | Provider: model=gpt-5.4
WARNING root: Invalid API response (retry 2/3): response.output is empty | Provider: model=gpt-5.4
ERROR root: Invalid API response after 3 retries.
DEBUG root: API Response received - Model: gpt-5.4, Usage: ResponseUsage(input_tokens=12716, output_tokens=23, reasoning_tokens=14)
DEBUG root: response.output_text='', status='completed', reasoning=Reasoning(effort='medium', summary='detailed')

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions