Skip to content

[Bug][Codex App] Encrypted subagent MESSAGE payload is surfaced as gAAAA ciphertext after adapter_eof #3021

Closed

Description

@yxr1995-maker

Client or integration

Codex App with delegated subagent messaging.

Area

OpenAI Responses adapter, encrypted tool output recovery, and subagent message delivery.

Summary

After a native OpenAI Responses turn ended with:

stream disconnected before completion: Incomplete response returned, reason: adapter_eof

a later message from the same delegated subagent was delivered into the parent conversation as a raw encrypted-looking payload beginning with gAAAA..., instead of the plaintext subagent result or a structured decryption error.

The parent message envelope was rendered normally (Message Type, Task name, Sender, and Payload), but the entire payload body was ciphertext.

The ciphertext is intentionally not included in this report. It may contain encrypted task output and should be treated as sensitive even when it is not immediately readable.

Reproduction

Observed sequence:

  1. Run a parent Codex App task through a local OpenCodex proxy using the native OpenAI Responses adapter.
  2. Spawn a delegated subagent.
  3. The parent turn encounters adapter_eof before receiving a complete response.
  4. Interrupt or follow up with the subagent while recovering the task.
  5. The parent later receives a subagent MESSAGE event whose payload is a long gAAAA... ciphertext value rather than plaintext.

This was observed once in a live task. The asynchronous timing makes it non-deterministic, so the exact request, thread, account, and ciphertext values are omitted.

Expected behavior

  • Encrypted subagent or function output is decrypted only in the owning account/session context before delivery.
  • If the required decryption context is unavailable after retry, fail closed with a structured error and correlation-safe diagnostics.
  • Never forward the encrypted blob to the parent conversation or user-visible UI.
  • Preserve enough non-sensitive diagnostics to distinguish account/session mismatch, missing decryption state, and ordinary stream truncation.

Actual behavior

The parent task received a syntactically valid subagent message envelope containing raw ciphertext as its visible payload. The intended subagent output was lost.

Impact

  • User-visible task output is corrupted and cannot be acted on.
  • An internal encrypted artifact crosses into the user-visible message surface.
  • Retrying a long task can preserve the wrong account/session context and make recovery confusing.
  • Users may accidentally paste the ciphertext into logs, issues, or support channels.

Environment

OpenCodex: 2.34.0
Codex CLI: 0.151.0-alpha.7.1
Client: Codex App
Provider adapter: openai-responses
Auth mode: forward
Codex account mode: pool
Local proxy: 127.0.0.1:10100
OS: macOS 26.6.1 (Build 25G76) on Apple Silicon (arm64)

The local OpenCodex health endpoint remained healthy during diagnosis. The service log contained other 502/429 records, but did not contain the literal strings adapter_eof or the surfaced ciphertext, so this report does not claim a proven log-level correlation.

Related behavior

#919 covers mid-stream OpenAI socket resets affecting account affinity. This report is distinct: encrypted subagent output itself reached the parent UI after an incomplete stream.

Security and redaction

  • Ciphertext omitted.
  • Tokens, API keys, account identifiers, request IDs, thread IDs, and timestamps omitted.
  • No configuration file or raw log is attached.

Checks

  • Searched existing issues for adapter_eof, stream disconnected, and encrypted/decryption errors.
  • Removed secrets, ciphertext, account details, request credentials, and personal data.
  • Clearly marked the reproduction frequency and current evidence boundary.
  • Added operating system info

Activity

  1. github-actions commented on Aug 30, 2026

    @github-actions
    Contributor

    Issue reopened

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

  2. lidge-jun commented on Aug 30, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 68 / 80

    이 이슈가 말하는 구멍은 지금 dev HEAD 870a2adb6 에도 그대로 열려 있다. 패키지 버전은 2.37.0 이다. 작성자가 본 설치본은 2.34.0 이다. 그 사이 #2850 과 보안 후속 #2851 이 올라왔다. 그 기차는 콤보/라우트 자식이 읽을 수 없는 NEW_TASK 암호문을 한 번 풀어 보거나, 못 풀면 닫는 길이다. 이 이슈가 그린 장면은 그 길이 아니다. 부모 Codex App 대화에 나중에 도착한 위임 서브에이전트 MESSAGE 의 Payload 가 gAAAA 로 시작하는 Fernet 암호문 그대로 보인 것이다.

    재현은 한 번이다. 네이티브 OpenAI Responses 턴이 stream disconnected before completion: Incomplete response returned, reason: adapter_eof 로 끊긴 뒤, 같은 위임 서브에이전트가 보낸 메시지가 부모 화면에 봉투(Message Type, Task name, Sender, Payload)는 멀쩡하고 Payload 몸통만 암호문이었다. 작성자는 openai-responses, auth forward, Codex account mode pool, 로컬 프록시 127.0.0.1:10100, macOS 26.6.1 이다. 암호문 자체는 이슈에 없다. 의도적으로 빼 두었다.

    HEAD 에서 adapter_eof 는 업스트림이 만든 코드가 아니다. src/bridge.ts 가 어댑터 스트림이 단말 이벤트 없이 끝나면 response.incomplete 에 reason: adapter_eof 를 붙여 합성한다. 잘린 스트림을 완료로 위장하지 않으려고 그렇게 한다. 그 합성 자체는 맞다. 문제는 그 다음이다. 잘린 턴 뒤에 도착한 서브에이전트 MESSAGE 를 부모가 읽을 수 있는 글이 아니라, 구조만 맞는 Fernet 토큰으로 보여 준 것이다.

    #2851 이 막아 둔 면은 입구다. hasUnreadableEncryptedAgentTask 는 요청 input 맨 끝 agent_message 만 본다. 라우팅 봉투 정규식 AGENT_MESSAGE_ROUTING_ENVELOPE 와 복구기의 ROUTING_HEADER 는 NEW_TASK 만 받는다. MESSAGE 는 처음부터 복구 대상이 아니다. 복구 함수 recoverEncryptedAgentTask 는 config.agentTaskRecovery.enabled === true 일 때만 살아 있다. 기본값은 꺼져 있다. 그리고 최종 라우트가 정규 OpenAI forward 이면 복구를 건너뛴다. 작성자 환경이 바로 그 길이다. 네이티브 백엔드는 암호문을 풀 수 있다고 보고, 라우트 공급자에게만 unreadable_encrypted_agent_task 400 을 준다. 부모 화면에 나가는 SSE 를 검사해서 Fernet Payload 를 삼키는 칸은 없다.

    sanitize 도 이 장면을 안 닫는다. sanitizeEncryptedContentInPlace 는 진짜처럼 생긴 백엔드 암호문(looksLikeBackendCiphertext)을 바이트 그대로 둔다. 평문만 input_text 로 고친다. Fernet 토큰 검출기(structurallyValidFernetTokens)는 gAAAA 모양의 버전 바이트 0x80 토큰을 이미 알아본다. 그런데 그 검출 결과는 콤보 자식 NEW_TASK 입구에만 쓰인다. 부모에게 보이는 MESSAGE 출력에는 안 붙는다. 그래서 계정/세션이 어긋나거나 잘린 스트림 뒤에 복호 문맥이 없으면, 앱은 구조화된 에러 대신 암호문 Payload 를 그린다.

    이건 #919 와 다르다. #919 는 소켓 리셋 뒤 계정 친화도다. 이 이슈는 친화도가 깨진 다음 암호문이 부모 UI 로 새는 면이다. #2744 는 #2850/#2851 로 닫힌 콤보 NEW_TASK 복구다. 중복으로 닫지 마라. 초안 PR #2496 은 평문 V2 협업 옵션이고, 열린 PR #2113 은 암호문 V2 를 신뢰 공급자에게 더 통과시키자는 쪽이다. #2113 을 이 구멍의 수리로 쓰면 더 넓어진다. types.ts/config.ts 분할과도 안 겹친다. 미리보기 배포는 계획에 없다. 이 구멍을 겨냥한 수리 PR 은 지금 없다.

    라인 src/bridge.ts 762 - 어댑터 스트림이 단말 없이 끝나면 response.incomplete / adapter_eof 를 합성한다. 작성자가 본 끊김 문구의 출처다. 합성 자체는 맞다.
    라인 src/server/responses/encrypted-payload.ts 178 - AGENT_MESSAGE_ROUTING_ENVELOPE 가 NEW_TASK 만 잡는다. MESSAGE 봉투는 복구/가독 검사 대상이 아니다.
    라인 src/server/responses/agent-task-recovery.ts 71 - 복구기 ROUTING_HEADER 도 NEW_TASK 만 받는다. 타입 칸이 NEW_TASK 로 고정되어 있어 MESSAGE 는 봉투로 안 잡힌다.
    라인 src/server/responses/agent-task-recovery.ts 42 - agentTaskRecoveryConfig 는 enabled === true 가 아니면 null 이다. 기본 설치에서는 복구 기차가 출발하지 않는다.
    라인 src/server/responses/core.ts 2913 - 네이티브 fallback 이 암호문을 먹을 수 있다고 보고, 최종 라우트 선택 뒤에만 복구한다. 정규 OpenAI forward 면 이 칸을 건너뛴다.
    라인 src/server/responses/core.ts 3047 - 읽을 수 없는 암호문 자식은 정규 OpenAI forward 가 아닐 때만 400 으로 닫는다. 작성자 환경(openai-responses + forward)은 여기로 안 떨어진다.
    라인 src/server/responses/encrypted-payload.ts 295 - sanitizeEncryptedContentInPlace 가 looksLikeBackendCiphertext 이면 슬롯을 그대로 둔다. 부모 화면용 Fernet 차단이 아니다.
    경로 src/server/responses/encrypted-payload.ts structurallyValidFernetTokens - gAAAA Fernet 모양은 이미 검출한다. 그 결과를 부모 MESSAGE 출력 경로에 연결한 코드는 없다.

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

    • 부모에게 보이는 MESSAGE 암호문을 복구 기차에 넣을지, 검출만 하고 구조화 에러로 닫을지. 복구를 MESSAGE 로 넓히면 평문 오라클이 커진다.
    • 네이티브 forward 가 업스트림으로는 암호문을 넘겨도, 클라이언트로 나가는 SSE/JSON 에서는 Fernet Payload 를 삼킬지
    • pool 계정 전환이 adapter_eof 뒤에 복호 문맥을 깨는지. [Enhancement]: Mid-stream OpenAI socket resets can break Codex account affinity #919 친화도와 같은 PR 로 묶을지, 출력 차단만 따로 둘지
    • 한 번뿐인 재현이라 로그 상관을 더 받을지, HEAD 코드만으로 출력 차단을 먼저 넣을지

    너의 추천
    열어 둔다. 라벨을 바꾸지 마라. #919 나 #2744 중복으로 닫지 마라. #2851 이 이 이슈를 고친 것으로 보지 마라. 그 기차는 꺼진 기본값의 NEW_TASK 입구이고, 작성자 길은 네이티브 forward 라 복구를 건너뛴다. 고치는 자리는 부모에게 보이는 에이전트 MESSAGE 출력이다. 구조가 맞는 Fernet 이 Payload 로 나가려 하면 unreadable_encrypted_agent_task 같은 구조화 에러로 닫아라. 암호문 문자열을 화면에 그리지 마라. recoverEncryptedAgentTask 를 MESSAGE 로 넓히지 마라. PR #2113 을 이 수리로 합치지 마라. types/config 분할에 안 깔리는 좁은 PR 로 dev 에 올려라.

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

  3. lidge-jun commented on Sep 1, 2026

    @lidge-jun
    Owner

    Landed on dev via #3116 at 5c0c131. Exact-head CI had 0 failures and 0 pending checks; an explicit security review confirmed that MESSAGE detection remains fail-closed and does not widen decryption. Closing this bug as fixed.

  4. github-actions commented on Sep 1, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

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

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions