Skip to content

组合使用deepseek模型时,failover丢失字段报错 #4696

Closed

Description

@hnyyghk

Client or integration

Codex CLI

Area

CLI

Summary

组合使用deepseek模型,failover丢失字段报错invalid_request_error

Reproduction

第一个模型报错upstream_server_error时,failover切换到第二个deepseek官方api模型,报错丢失字段:
Provider error 400: {"error":{"message":"The reasoning_text in the thinking mode must be passed back to the API.","type":"invalid_request_error","param":null,"code":"invalid_request_error"}}
Image

Version

2.54.0

Operating system

macOS 13.5.1

Provider and model

No response

Logs or error output

Screenshots and supporting files

No response

Redacted configuration

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. added
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    cliCLI, config inject, packaging flags
    on Sep 15, 2026
  2. github-actions commented on Sep 15, 2026

    @github-actions
    Contributor

    Issue reopened

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

  3. lidge-jun commented on Sep 15, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 63 / 80

    설명

    이 이슈는 Codex CLI에서 DeepSeek 모델을 콤보(조합)로 묶었을 때, 첫 번째 타깃이 upstream_server_error로 실패하고 콤보 failover가 두 번째 DeepSeek 공식 API 모델로 넘어간 뒤, 그 다음 요청이 Provider error 400 — The reasoning_text in the thinking mode must be passed back to the API. 로 죽는다는 보고입니다. 작성자 환경은 2.54.0 / macOS / Codex CLI이고, 재현 칸은 비어 있지만 스크린샷과 정확한 업스트림 문구가 붙어 있습니다. 품질 봇이 재현 단계 공백으로 이슈를 자동 닫았지만(not_planned), 에러 문자열과 콤보 failover 서술은 이미 알려진 DeepSeek thinking/Responses 계열 결함과 맞춰 볼 수 있어서, 메인테이너 입장에서는 닫힌 채 방치하기보다 재오픈·보강 후 추적하는 편이 맞습니다.

    현재 추적 중인 dev HEAD는 5dfea7235deb5942f9a5c657e075fed0fa388fad 이고 package.json은 2.57.0입니다(#4686으로 2.56.0 제품 동결 386303af1 이후 열린 버전). 2.56.0 promote 레인(#4694)과 OAuth 429 사다리/#4690 reauth backoff는 이 이슈의 직접 원인이 아닙니다. 이 보고의 핵심 축은 (1) 콤보가 upstream_server_error에서 의도적으로 hop 하는 정책과, (2) DeepSeek thinking/Responses가 이전 턴의 plaintext reasoning을 다시 요구하는 계약이, 타깃이 바뀐 뒤에도 깨지지 않는지입니다.

    코드로 보면 콤보 실패 판정은 src/combos/failover.ts의 comboFailureDecision 입니다. 상태코드 5xx뿐 아니라 구조화 코드 upstream_server_error 도 명시적으로 "hop" 을 반환합니다. 실제 hop 루프는 src/server/responses/core-combo.ts 에서 자식 요청을 다시 만들고(concreteComboRequestBody / buildComboChildHeaders), consumeComboFailure 뒤 failureDecision === "hop" 이면 다음 타깃으로 갑니다. 즉 ‘첫 DeepSeek가 upstream_server_error → 두 번째 DeepSeek로 전환’ 자체는 버그가 아니라 현재 설계입니다. 문제는 hop 이후 두 번째 타깃으로 나가는 히스토리에 thinking 필드가 빠질 때입니다.

    DeepSeek 공식 레지스트리(src/providers/registry/entries-core.ts, id deepseek)는 preserveResponsesReasoningContent: true 와 preserveReasoningContentModels: DEEPSEEK_NATIVE_THINKING_MODELS(deepseek-flash / deepseek-v4-flash)를 심어 둡니다. Responses 경로에서는 src/adapters/openai-responses/passthrough.ts 가 sanitizeReasoningInputContent(src/adapters/openai-responses/reasoning.ts)를 호출하고, preserve 플래그가 켜져 있을 때만 plaintext reasoning_text content를 비우지 않습니다. Chat 경로에서는 src/adapters/openai-chat/messages.ts 가 reasoning_content 를 되살리고, 캐시 미스 시 placeholder를 넣는 방어(#950/#1193 계열)가 있습니다. 캐시 본체는 src/responses/reasoning-replay-cache.ts 인데, 키에 provider/model/credential identity가 들어가므로 콤보 hop으로 serving identity가 바뀌면 peekReasoningForCall 이 빗나갈 수 있습니다. 같은 파일/ src/server/responses/core-replay.ts 쪽에서 identity 변경 시 _stripReasoningEncryptedContent 가 켜지면 opaque blob은 의도적으로 벗겨지지만, 이미 클라이언트 히스토리에 plaintext reasoning_text 가 없거나 이전 타깃에서 blanked 된 상태면 공식 DeepSeek는 바로 이번 400을 냅니다.

    과거에 같은 업스트림 문구로 닫힌 이슈가 있습니다: #1477 / #1534 (DeepSeek V4 Flash Responses continuation에서 reasoning_text 유실 → 400), #2106 (다른 원인으로 스트림이 끊긴 뒤 reasoning replay 400). 그것들은 대체로 단일 프로바이더·도구 연속 턴 쪽이었고, 이번 #4696은 ‘콤보 failover hop 직후’라는 축이 새로 붙습니다. 지금 열린 이슈 목록에서 동일 제목/동일 재현의 중복은 보이지 않았고, #4688(DeepSeek 카탈로그 id 불일치)은 다른 축입니다. types.ts/config.ts 대분할로 무효화되는 PR 이슈도 아닙니다. 보고 버전 2.54.0과 현재 2.57.0/dev 사이에 콤보+DeepSeek reasoning 전용 수정이 동결 트리에 뚜렷이 잡히지 않으므로, ‘이미 고쳤다’고 닫기보다 현재 HEAD 기준으로 재현·회귀를 확인하는 쪽이 정직합니다.

    src/combos/failover.ts · comboFailureDecision - upstream_server_error 는 hop 이 맞고, 이 이슈의 1단계(전환 자체)는 정상 경로다
    src/server/responses/core-combo.ts · 자식 재디스패치 - hop 시 요청 본문/히스토리를 다음 DeepSeek 타깃으로 넘기는데, thinking 필드 보존 보장이 콤보 경계에 명시되어 있지 않다
    src/adapters/openai-responses/reasoning.ts · sanitizeReasoningInputContent - preserveResponsesReasoningContent 가 꺼지거나 히스토리에 reasoning_text 가 비어 있으면 content=[] 로 비워 DeepSeek thinking 400을 유발할 수 있다
    src/adapters/openai-responses/passthrough.ts · preserveRawReasoningContent / stripEncryptedContent - 타깃 identity 변경 시 opaque strip 과 plaintext keep 이 엇갈리면 hop 직후 공식 API만 400이 난다
    src/responses/reasoning-replay-cache.ts · keyFor / peekReasoningForCall - 캐시 키가 provider·model·credential 에 묶여 있어 콤보 hop 후 Chat 경로 재부착이 미스날 수 있다
    src/adapters/openai-chat/messages.ts · reasoning_content / placeholder - Chat 쪽은 미스 시 공백 placeholder 방어가 있으나, 보고된 에러 문구는 Responses reasoning_text 계약이라 Chat 방어만으로는 커버되지 않는다
    src/providers/registry/entries-core.ts · deepseek preserveResponsesReasoningContent - 공식 deepseek 엔트리는 preserve=true 이지만, 콤보 첫 타깃이 다른 DeepSeek류(게이트웨이/커스텀)면 플래그·와이어 불일치로 히스토리가 이미 빈 content 일 수 있다
    이슈 본문 Reproduction - 품질 봇이 단계 공백으로 자동 닫음; 스크린샷+에러 문구는 충분해 보이므로 재오픈·보강이 필요하다

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

    너의 추천

    중복으로 닫지 말고 유효 버그로 재오픈(또는 작성자 보강 후 자동 재오픈)하세요. 다음 스텝은 (1) 콤보 타깃 두 개의 정확한 provider/model 과 와이어(openai-responses vs openai-chat) 확인, (2) 현재 dev 에서 ‘DeepSeek A upstream_server_error → DeepSeek B’ hop 직후 tool-call continuation 에 reasoning_text / reasoning_content 가 살아 있는지 회귀 테스트 추가, (3) Chat placeholder와 대칭으로 Responses 쪽에도 hop 후 빈 thinking 을 막거나, 히스토리에 thinking 이 없으면 그 타깃으로는 hop 하지 않도록 좁히는 패치입니다. 2.56.0 릴리즈 트레인과는 분리해 추적하는 것을 추천합니다.

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

  4. hnyyghk commented on Sep 15, 2026

    @hnyyghk
    Author

    所以这个现象是bug吗,预计什么时候可以修复?

  5. hnyyghk commented on Sep 15, 2026

    @hnyyghk
    Author
  6. added a commit that references this issue on Sep 16, 2026
  7. lidge-jun commented on Sep 16, 2026

    @lidge-jun
    Owner

    Landed via #4769 at 53455e0

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

    account-poolOAuth, credentials, Codex pool, quota, failover, plansbugSomething isn't workingcliCLI, config inject, packaging flagslanded-via-maintainerOriginal PR closed after landing via a maintainer merge train

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions