You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: an account change does not scrub uploaded file_id references it classifies as non-portable #4710
src/server/responses/account-change-state.ts detects file_id/file_ids in a request (:85-109, :133-144) and classifies that state as non-portable across an account change (:208-218).
scrubUnportableConversationStateInPlace() then removes only previous_response_id and conversation (:152-171). A request whose only account-bound state is an uploaded file reference returns scrubbed === false and is replayed unchanged against the new account, which may not be able to read that file.
Existing tests exercise the classifier, or bodies that also contain previous_response_id; the file-only path is not covered. The gap predates 2.56.0, but it leaves the advertised safety fix of 2b43c14c03 incomplete.
Reproduction
Send a pooled request that references an uploaded file by file_id and carries no previous_response_id and no provider conversation id.
Cause routing to change the serving account for that turn.
The request is forwarded unchanged; the new account receives a reference this module already declared account-bound.
Suggested fix: fail closed or retain the issuing account when uploaded-file references are present. If dropping them is the chosen contract, remove every detected reference explicitly and cover the file-only case through applyAccountChangeConversationStateScrub().
이 이슈는 계정 변경 스크럽이 업로드 file_id를 ‘이식 불가’로 분류해 놓고도 실제로 지우지 않는 구멍이다. 현재 dev HEAD 51d577c3f(패키지 2.57.0)의 src/server/responses/account-change-state.ts를 보면 collectFileIds(약 85–109행)가 file_id/file_ids를 모으고, 비이식 판정(약 208–218행대 canPortConversationState 경로)에 그 결과가 들어간다. 그런데 scrubUnportableConversationStateInPlace(152–171행)는 previous_response_id와 conversation만 지운다. 주석도 ‘continuation state naming server-side objects’로 그 둘만 소유한다고 적혀 있다.
그래서 본문에 업로드 파일 참조만 있고 previous_response_id가 없으면 scrubbed === false가 되어 applyAccountChangeConversationStateScrub가 조기 반환한다. 분류기는 이미 account-bound라고 했는데 요청은 새 계정으로 그대로 나간다. #4641/2b43c14c0이 광고한 계정 변경 안전 계약이 파일 전용 경로에서는 반만 닫힌 상태다. 기존 테스트는 분류기나 previous_response_id가 같이 있는 몸을 위주로 보이며, file-only 경로는 빠져 있다는 이슈 설명이 tip 코드와 맞다.
수정 방향은 두 갈래다. (1) 파일 참조가 있으면 fail-closed로 발행 계정을 유지하거나 요청을 거절한다. (2) 드롭이 계약이면 감지한 모든 file_id/file_ids를 명시적으로 제거하고 applyAccountChangeConversationStateScrub에 file-only 회귀 테스트를 붙인다. encrypted reasoning/compaction을 건드리지 말라는 기존 주석(#2247 계약)은 유지해야 한다.
라인 src/server/responses/account-change-state.ts 85–109 - collectFileIds가 파일 참조를 모은다.
라인 src/server/responses/account-change-state.ts 152–171 - 스크럽이 previous_response_id/conversation만 삭제하고 file_id는 손대지 않는다.
라인 src/server/responses/account-change-state.tsapplyAccountChangeConversationStateScrub - scrubbed === false면 경고·로그 없이 본문 그대로 통과.
경로 테스트 - file-only account-change 케이스가 빠져 계약이 CI에 고정되지 않았다.
메인테이너의 판단이 필요한 지점
파일 참조가 있을 때 fail-closed(계정 유지/거절) vs 명시적 scrub 중 무엇으로 계약을 닫을지.
너의 추천
받아라. 기본 추천은 감지된 file 참조를 명시적으로 제거하고 file-only 회귀 테스트를 applyAccountChangeConversationStateScrub에 붙이는 쪽이다. fail-closed가 더 안전하면 그 계약을 주석·구조 문서에 먼저 고정하라. types/config 스플릿과 무관하다.
Activity
lidge-jun commentedon Sep 15, 2026
리뷰 · 우선순위 72 / 80
이 이슈는 계정 변경 스크럽이 업로드
file_id를 ‘이식 불가’로 분류해 놓고도 실제로 지우지 않는 구멍이다. 현재devHEAD51d577c3f(패키지 2.57.0)의src/server/responses/account-change-state.ts를 보면collectFileIds(약 85–109행)가file_id/file_ids를 모으고, 비이식 판정(약 208–218행대canPortConversationState경로)에 그 결과가 들어간다. 그런데scrubUnportableConversationStateInPlace(152–171행)는previous_response_id와conversation만 지운다. 주석도 ‘continuation state naming server-side objects’로 그 둘만 소유한다고 적혀 있다.그래서 본문에 업로드 파일 참조만 있고
previous_response_id가 없으면scrubbed === false가 되어applyAccountChangeConversationStateScrub가 조기 반환한다. 분류기는 이미 account-bound라고 했는데 요청은 새 계정으로 그대로 나간다. #4641/2b43c14c0이 광고한 계정 변경 안전 계약이 파일 전용 경로에서는 반만 닫힌 상태다. 기존 테스트는 분류기나previous_response_id가 같이 있는 몸을 위주로 보이며, file-only 경로는 빠져 있다는 이슈 설명이 tip 코드와 맞다.수정 방향은 두 갈래다. (1) 파일 참조가 있으면 fail-closed로 발행 계정을 유지하거나 요청을 거절한다. (2) 드롭이 계약이면 감지한 모든
file_id/file_ids를 명시적으로 제거하고applyAccountChangeConversationStateScrub에 file-only 회귀 테스트를 붙인다. encrypted reasoning/compaction을 건드리지 말라는 기존 주석(#2247 계약)은 유지해야 한다.라인
src/server/responses/account-change-state.ts85–109 -collectFileIds가 파일 참조를 모은다.라인
src/server/responses/account-change-state.ts152–171 - 스크럽이previous_response_id/conversation만 삭제하고 file_id는 손대지 않는다.라인
src/server/responses/account-change-state.tsapplyAccountChangeConversationStateScrub-scrubbed === false면 경고·로그 없이 본문 그대로 통과.경로 테스트 - file-only account-change 케이스가 빠져 계약이 CI에 고정되지 않았다.
메인테이너의 판단이 필요한 지점
너의 추천
받아라. 기본 추천은 감지된 file 참조를 명시적으로 제거하고 file-only 회귀 테스트를
applyAccountChangeConversationStateScrub에 붙이는 쪽이다. fail-closed가 더 안전하면 그 계약을 주석·구조 문서에 먼저 고정하라. types/config 스플릿과 무관하다.이 댓글은 grok-bot이 작성했습니다
test(responses): pin the account-change half of the incident (#4546)
fix(responses): refuse an account move that carries an uploaded file (#…
fix(responses): refuse the uploaded-file move on the alternate-accoun…
Merge pull request #4777 from lidge-jun/codex/cx3-account-change-file…
lidge-jun commentedon Sep 16, 2026
Landed via #4777 at 44392b5 (on tip 9fc7e9b with #4764/#4765).
devis not the default branch so GitHub did not auto-close from the PR's Closes line.이 댓글은 grok-bot이 작성했습니다
test(responses): pin the account-change half of the incident (lidge-j…
fix(responses): refuse an account move that carries an uploaded file (l…
fix(responses): refuse the uploaded-file move on the alternate-accoun…
Merge pull request lidge-jun#4777 from lidge-jun/codex/cx3-account-ch…