Skip to content

OpenAIResponsesCompactionSession: compacted items fail replay with "Unknown parameter: 'input[N].created_by'" #5162

Description

@omikader

Bug Summary

After OpenAIResponsesCompactionSession compacts a session's history, the very next turn fails with a 400 on the Responses API:

openai.BadRequestError: Error code: 400 - {'error': {'message': "Unknown parameter: 'input[1].created_by'.", 'type': 'invalid_request_error', 'param': 'input[1].created_by', 'code': 'unknown_parameter'}}

This happens on the turn immediately following a successful compaction, every time — the compacted history is effectively unusable once produced.

Root cause

responses.compact() returns a ResponseCompactionItem (the output type), which carries a created_by field (openai/types/responses/response_compaction_item.py). But the input type for replaying a compaction item, ResponseCompactionItemParam (openai/types/responses/response_compaction_item_param.py), has no created_by field at all — only encrypted_content, type, id.

OpenAIResponsesCompactionSession._run_compaction_locked stores the raw output items (via _normalize_compaction_output_items, which just does item.model_dump(exclude_unset=True, warnings=False) — created_by survives because it's explicitly set on the constructed object, not merely a default). Those stored items later get replayed as input for the next turn via prepare_input_with_session() in agents/run_internal/session_persistence.py:399:

strip_internal_input_item_metadata(ensure_input_item_format(item)) for item in history

strip_internal_input_item_metadata() (agents/run_internal/items.py:715) only strips TOOL_CALL_SESSION_DESCRIPTION_KEY and TOOL_CALL_SESSION_TITLE_KEY today — created_by isn't in that list, so it rides along into the next request's input array and gets rejected.

Prior art

This is the same class of bug as #1911 (closed as a duplicate of #1882/#1883) — an output-only field leaking unstripped into replayed input, just for a different field (status).

It's also already fixed in the sibling JS SDK: openai/openai-agents-js#1744 ("fix(core): strip created_by from RunItem replay", merged by @seratch, who's also the author of the two most recent Python-side compaction fixes — #… preserve concurrent writes during compaction (0.22.1), normalize compacted Responses user inputs before session reuse (0.14.2)). The JS fix strips created_by from top-level items, providerData, and shell output chunks at the same "replay boundary" concept strip_internal_input_item_metadata() already exists for on the Python side — it just doesn't cover this field yet.

Repro

"""
Reproduces: BadRequestError - Unknown parameter: 'input[1].created_by' (code: unknown_parameter)
on the second turn of a run using OpenAIResponsesCompactionSession.

Requires: pip install openai==3.16.2 openai-agents[redis]==0.22.3
Set OPENAI_API_KEY in environment.
"""
import asyncio
from agents import Agent, OpenAIResponsesModel, RunConfig, Runner, function_tool
from agents.memory.openai_responses_compaction_session import OpenAIResponsesCompactionSession
from agents.memory.sqlite_session import SQLiteSession
from openai import AsyncOpenAI


@function_tool
async def lookup(question: str) -> str:
    return "42"


agent = Agent(name="test", instructions="Answer using the lookup tool.", tools=[lookup])


async def main():
    client = AsyncOpenAI()
    underlying = SQLiteSession(session_id="repro-created-by")

    session = OpenAIResponsesCompactionSession(
        session_id="repro-created-by",
        underlying_session=underlying,
        client=client,
        model="gpt-5",
        compaction_mode="input",
        should_trigger_compaction=lambda ctx: len(ctx["compaction_candidate_items"]) >= 2,
    )

    run_config = RunConfig(model=OpenAIResponsesModel("gpt-5", openai_client=client), tracing_disabled=True)

    # Turn 0 succeeds and auto-triggers compaction (threshold met). The stored
    # compaction item retains `created_by`, copied straight from the output type.
    result = await Runner.run(agent, "How many are there?", session=session, run_config=run_config)
    print("turn 0:", result.final_output)

    # Turn 1 fails: the stored compaction item (with `created_by` still attached)
    # gets replayed as `input`, and the Responses API rejects it.
    result = await Runner.run(agent, "And how many of those are urgent?", session=session, run_config=run_config)
    print("turn 1:", result.final_output)


if __name__ == "__main__":
    asyncio.run(main())

Debug information

  • Agents SDK version: 0.22.3 (latest on PyPI at time of filing)
  • openai version: 3.16.2 (latest on PyPI at time of filing)
  • Model: reproduced via a corporate Azure OpenAI-backed gateway (litellm) fronting the model; the bug itself is client-side (the SDK constructs the invalid input payload before it ever reaches any provider), so it should reproduce identically against the direct OpenAI API — I haven't verified against a bare OPENAI_API_KEY myself, flagging that as unconfirmed rather than assuming.

Expected behavior

A turn following a compaction should replay cleanly, the same way the JS SDK now handles it after #1744.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions