Skip to content

[openai-responses] Fold role:"system" in input to top-level instructions and strip truncation parameter #2668

Closed

Description

@Liang-Psych

Client or integration

VS Code / GitHub Copilot (via /v1/responses BYOK endpoint)

Provider or upstream service

ChatGPT Codex forward / openai-responses

OpenCodex version

2.33.0

Endpoint or capability

/v1/responses, OpenAI Responses adapter

Current behaviour

When VS Code / Copilot sends a standard prompt to gpt-5.6-luna (or other native OpenAI passthrough models), the client formats system prompts as { type: "message", role: "system", content: [...] } within input, and includes "truncation": "disabled".

OpenCodex forwards this payload directly to the ChatGPT Codex backend, which returns HTTP 502 with:

upstreamError: System messages are not allowed

or

Unsupported parameter: truncation

As a result, VS Code Copilot fails on turn start with generic code: 0 / "Sorry, your request failed".

Expected behaviour

The openai-responses forward adapter should automatically sanitize the incoming Responses payload before dispatching:

  1. Extract any role: "system" items from input, join their text content, and fold them into the top-level instructions field (string or text block).
  2. Strip the unsupported truncation parameter from the forward body.

Minimal redacted request or reproduction

  1. Send a request to POST http://127.0.0.1:7861/v1/responses:
{
  "model": "gpt-5.6-luna",
  "store": false,
  "stream": true,
  "truncation": "disabled",
  "input": [
    {
      "type": "message",
      "role": "system",
      "content": [{ "type": "input_text", "text": "You are a helpful assistant." }]
    },
    {
      "type": "message",
      "role": "user",
      "content": [{ "type": "input_text", "text": "hi" }]
    }
  ]
}
  1. Request fails with HTTP 502 (System messages are not allowed).

Actual response or error

{
  "status": 502,
  "errorCode": "upstream_server_error",
  "upstreamError": "System messages are not allowed"
}

Upstream documentation

https://platform.openai.com/docs/api-reference/responses

Suggested mapping or implementation notes

In src/adapters/openai-responses.ts:

  • Add a helper foldSystemMessagesIntoInstructions(body) that scans body.input, moves role === "system" contents to body.instructions, and keeps only non-system items in input.
  • Add stripUnsupportedTruncation(body) to drop body.truncation.
  • Insert them into the sanitizedBody processing pipeline in createResponsesPassthroughAdapter.

Checks

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

Activity

  1. github-actions commented on Aug 26, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  2. Liang-Psych commented on Aug 26, 2026

    @Liang-Psych
    ContributorAuthor

    Updated issue report with the missing upstream documentation link (https://platform.openai.com/docs/api-reference/responses). Requesting automated check / review.

  3. lidge-jun commented on Aug 26, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 71 / 80

    설명

    이 이슈는 VS Code / GitHub Copilot이 OpenCodex의 /v1/responses 로 표준 Responses 요청을 보낼 때, input 배열 안의 role "system" 메시지와 맨 위 truncation 값이 ChatGPT Codex 앞단에서 HTTP 502를 내는 문제입니다. 업스트림이 돌려주는 말은 System messages are not allowed 또는 Unsupported parameter: truncation 입니다. 재현 JSON은 짧고 분명합니다. store false, stream true, truncation disabled, 그리고 system 한 줄과 user 한 줄입니다.

    지금 dev 58f5a29 는 이 두 값을 받아 주기만 하고, Codex로 보내기 전에 고치지 않습니다. src/responses/schema.ts 45-49줄의 systemMessageItemSchema 는 role system 입력 항목을 허용합니다. 같은 파일 168줄은 truncation 을 아무 값이나 받게 되어 있습니다. 146줄의 instructions 는 이미 문자열 또는 null 입니다. 그래서 프록시는 400을 내지 않고, 잘못된 몸을 그대로 앞으로 넘깁니다. 그 결과 클라이언트는 code 0 과 Sorry, your request failed 만 보게 됩니다.

    앞으로 보내는 살균은 src/adapters/openai-responses.ts 1714줄 createResponsesPassthroughAdapter 안에 있습니다. 1791-1792줄의 forward 경로에서 stripUnsupportedForwardParams 를 호출합니다. 그 함수는 1098-1104줄에서 max_output_tokens 와 metadata 만 빼고, truncation 은 빼지 않습니다. 캐논 ChatGPT 앞에만 쓰는 stripDeprecatedPromptCacheRetention 은 1795-1796줄에 따로 있고, 이것도 다른 필드입니다. 호환 목록 src/compatibility/openai-responses.ts 는 max_output_tokens, metadata, prompt_cache_retention 만 문서로 남겨 두었고, system 역할과 truncation 주장은 없습니다.

    1862줄의 sanitizedBody 체인은 compaction 항목 청소, hosted tools 제거, 잘못된 item id 제거, 저장하지 않을 때 id 제거, Spark 호환, tool schema 정규화만 합니다. 이슈가 제안한 foldSystemMessagesIntoInstructions 나 stripUnsupportedTruncation 이라는 이름은 이 파일에 없습니다. 따라서 system 항목은 input 에 남고, truncation 도 몸에 남습니다.

    공개 OpenAI Responses API 는 truncation 을 지원합니다. 모든 openai-responses 목적지에 무조건 지우면, API 키로 공개 API를 쓰는 사람은 깨집니다. 이미 같은 파일이 그 경계를 알고 있습니다. 1795줄은 isCanonicalOpenAiForwardProvider 가 참일 때만 폐기된 캐시 필드를 뺍니다. truncation 과 system 접기도 그 경계 안에 두는 것이 맞습니다. VS Code Copilot 은 이 저장소에서 github-copilot 제공자와는 다른 이야기입니다. 여기는 Copilot을 클라이언트로 써서 Codex 모델을 빌리는 경로입니다.

    system 을 instructions 로 접을지, developer 역할로 바꿀지도 정해야 합니다. src/server/responses/collaboration.ts 485줄은 이미 system 과 developer 를 같은 앞머리로 봅니다. 여러 system 항목과 이미 있는 instructions 를 어떤 순서로 붙일지, 도구 이어가기 턴에서 매번 접을지도 테스트가 필요합니다. 이슈의 제안처럼 텍스트만 모아 instructions 뒤에 붙이고 input 에서 system 항목만 빼는 길이, 기존 살균 함수 스타일과 잘 맞습니다.

    src/adapters/openai-responses.ts:1098 - stripUnsupportedForwardParams 는 max_output_tokens 와 metadata 만 빼서, Codex 가 모르는 truncation 이 그대로 갑니다.

    src/responses/schema.ts:47 - role system 을 받아 주므로 프록시는 통과시키고, Codex 만 System messages are not allowed 로 502를 냅니다.

    src/adapters/openai-responses.ts:1862 - sanitizedBody 체인에 system 접기와 truncation 제거가 없어 이슈의 최소 요청이 살균되지 않습니다.

    src/compatibility/openai-responses.ts - Codex forward 비지원 목록에 truncation 과 system 역할이 없어, 이 구멍이 호환 계약에도 안 적혀 있습니다.

    메인테이너의 판단이 필요한 지점

    • 변환을 ChatGPT Codex forward 에만 둘지, 모든 openai-responses 목적지에 둘지 정해야 합니다.
    • system 을 맨 위 instructions 로 접을지, developer 메시지로 바꿀지 정해야 합니다.
    • 이미 instructions 가 있을 때 새 system 텍스트를 앞에 둘지 뒤에 둘지, 여러 system 항목을 어떻게 이을지 정해야 합니다.
    • VS Code Copilot 클라이언트를 공식 호환 대상으로 삼을지, 이번 고침은 일반 Responses 살균으로만 둘지 정해야 합니다.

    너의 추천

    이 이슈는 열어 두고 구현을 진행하는 것을 추천합니다. 고침은 src/adapters/openai-responses.ts 의 forward 살균에 좁게 넣으세요. isCanonicalOpenAiForwardProvider 가 참일 때만 truncation 을 빼고, input 의 role system 텍스트를 기존 instructions 뒤에 붙인 다음 system 항목만 제거하세요. 공개 API 경로의 truncation 은 그대로 두세요. 테스트는 이슈의 최소 JSON이 Codex forward 에서 system 과 truncation 이 빠지고, API 키 경로에서는 truncation 이 남는지를 고정하세요. 호환 목록에도 같은 두 주장을 한 줄씩 추가하세요.

    이 댓글은 grok-bot이 작성했습니다

  4. Ingwannu commented on Aug 26, 2026

    @Ingwannu
    Owner

    Confirmed on current dev (779b6090c): the Responses schema accepts input role: "system" and truncation, but the canonical ChatGPT Codex forward sanitizer does not normalize either before dispatch.

    I am going to fix this with a destination-scoped transform, not a global Responses rewrite:

    • only canonical ChatGPT Codex forward removes truncation; key-auth/public OpenAI-compatible destinations keep it;
    • fully textual system messages in input are folded into top-level instructions in stable order and removed from input;
    • existing instructions are preserved and combined deterministically;
    • non-system items, tool call_id values, and the rest of the request remain unchanged;
    • focused regressions will cover multiple system messages, existing instructions, the canonical-forward boundary, and the public/key-auth preservation path;
    • the Responses compatibility contract will be updated with the same scoped behavior.

    I will link the implementation PR here and keep this issue open until the fix lands on dev and the exact-head checks are green.

  5. Ingwannu commented on Aug 26, 2026

    @Ingwannu
    Owner

    Implementation is now available as draft PR #2672 at exact head 50b50e1b9a35095ebf62fe214fef6691ab71275c.

    The destination boundary, public/key-auth preservation controls, atomic multimodal behavior, compatibility fixture, and public docs are included. Focused tests, the existing passthrough suite, typecheck, privacy scan, docs build, and diff check pass. The full local suite reached 14,972 pass / 16 skip with one unrelated load-sensitive Cursor executor failure that passes in isolation, so the PR remains draft pending exact-head CI and another maintainer review. This issue stays open until the PR lands on dev.

  6. Ingwannu commented on Aug 26, 2026

    @Ingwannu
    Owner

    Update: draft PR #2672 exact-head CI is now fully green across all Linux shards, macOS, gates, keyring, npm-global jobs, React Doctor, and the aggregate ci check. The PR remains draft because I am the author and cannot self-approve; lidge-jun review has been requested. The issue stays open until the change reaches dev.

  7. added a commit that references this issue on Aug 27, 2026
    2a9c18d
  8. lidge-jun commented on Aug 27, 2026

    @lidge-jun
    Owner

    Landed via #2720 at 2feffbd (via #2672 on L1). Fix is on dev: canonical forward prompt envelope normalization.

  9. added a commit that references this issue on Sep 14, 2026
    020db72
  10. added a commit that references this issue on Sep 17, 2026
    3580ed2
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

    landed-via-maintainerOriginal PR closed after landing via a maintainer merge trainproviderProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reports

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions