제가 다른 일을 하는 동안에도 블로그 소식 점검과 글쓰기가 이어지도록 코덱스를 예약 실행해 뒀어요. 그런데 2026년 10월 7일 오후 8시와 10시, 뉴스 작업 두 번이 글을 쓰기도 전에 멈췄고 각 실행에서 명령 실행이 두 차례씩 차단됐어요.
앱과 별도로 실행한 CLI
이 블로그에서는 Codex가 자료를 읽고 원고를 쓰면 Claude가 최종 검수하고, 실행기가 검사와 공개를 이어 맡아요. 예약 작업은 사람이 대화하는 앱과 별도로 CLI, 즉 명령줄에서 실행하는 프로그램을 불러요. 파일을 읽고 고치는 명령은 샌드박스라는 제한된 실행 환경 안에서 돌아가고요. 이날은 원고가 검사에 걸린 게 아니라 그 환경이 시작되는 단계에서 멈췄어요. 따로 띄운 CLI가 실행 중인 앱의 파일에 손을 대면서, 글쓰기 전에 문서 읽기부터 막힌 상황이었어요.
처음에는 앱을 다시 켜면 풀릴 문제로 봤어요
오후 8시 실행 20261007200002-news의 결과에는 이렇게 남았어요.
실행 환경 오류로 명령 실행이 두 차례 차단돼 필수 문서와 기존 글을 읽지 못했습니다. 파일 수정과 검증은 수행하지 않았습니다.
원고를 쓰다가 중단한 것이 아니라, 쓰기 규칙과 기존 글을 읽는 첫 단계에서 멈췄다는 뜻이에요. 그 상태에서는 문장이나 자료를 손봐도 다음 단계로 갈 길이 없었어요.
초기의 운영 점검은 샌드박스 폴더가 올바르게 연결돼 있는지를 봤어요. 폴더 연결이 존재하는 것과 그 안에서 명령을 실행할 수 있는 것은 다른 문제였어요. 그래서 실제로 무해한 Node 명령을 실행해 보는 점검이 추가됐어요. Node는 자바스크립트 코드를 실행하는 프로그램이에요. 이 점검은 모델에 글을 부탁하지 않고, 명령 하나가 제한된 환경에서 시작되는지 확인했어요.
오후 9시 27분 점검에서 그 실행이 실패했고, 오류에는 runtime read/execute validation failed가 남았어요. 프로그램을 돌리는 데 필요한 파일을 읽고 실행할 권한을 확인하다가 막혔다는 말이에요. 더 구체적인 흔적은 앱이 쓰는 cua_node/bin/node_repl.exe의 권한을 갱신하려다 os error 32가 발생했다는 것이었어요. 그때 샌드박스 로그를 열어 보니 권한을 바꿀 대상 파일을 여는 단계에서 실패했고, 다른 프로세스 6개가 같은 실행 파일을 쓰고 있었어요. os error 32는 Windows에서 "다른 프로세스가 파일을 사용 중이라 열 수 없다"는 뜻이에요. 혹시 다른 설정 탓인가 싶어 환경 변수를 빼고 일부 기능을 꺼서 다시 시작해 봤지만 결과는 같았어요.
오후 10시 뉴스 실행도 같은 시작 단계에서 멈췄어요. 오후 10시 30분 운영 진단은 “코드 수정으로는 풀리지 않아 사용자가 Codex 앱을 재시작해야 합니다.”라고 판단했어요. 당시에는 실행 중인 앱을 내려 파일 사용을 풀면 된다고 본 거예요. 하지만 앱을 재시작한 뒤에도 앱에 들어 있는 Codex 0.162.0-alpha.2에서 같은 파일의 권한 갱신이 실패했어요. 재시작은 충돌을 피하는 데 충분하지 않았고, CLI가 왜 앱 파일을 찾아가는지로 질문이 바뀌었어요.
CLI가 앱 런타임까지 검사하고 있었어요
런타임은 프로그램이 실제로 코드를 실행할 때 쓰는 파일과 환경을 말해요. ACL은 파일마다 누가 읽고 실행하거나 바꿀 수 있는지 적어 둔 접근 권한 목록이고요. 이번에는 CLI가 사용할 필요가 없는 앱 런타임 파일까지 샌드박스 준비 과정의 권한 검사 대상에 들어갔어요.
코덱스가 공개된 Codex 소스 코드에서 Windows 샌드박스를 준비하는 부분(windows-sandbox-rs)을 읽어 보니, 앱 런타임 파일 전체를 검사하고 MAXIMUM_ALLOWED로 여는 경로가 있었어요. 이것은 허용되는 최대 접근 권한을 요청해 파일을 여는 방식이에요. 그 경로에 CLI도 들어갔고, 앱이 실행 중인 파일의 ACL을 갱신하려다 충돌했어요. 명령줄 프로그램을 별도로 띄웠다는 사실만으로 데이터 경로까지 갈라진 건 아니었어요.
수정은 전용 CLI 자식 프로세스의 LOCALAPPDATA를 나누는 방식이었어요. LOCALAPPDATA는 Windows 프로그램이 사용자별 로컬 데이터를 둘 위치를 알려 주는 환경 변수예요. 기존 환경을 복사한 뒤, 해당 CLI에 넘길 값만 아래 한 줄로 바꿨어요.
env.LOCALAPPDATA = join(env.LOCALAPPDATA, 'Nanoplaydays', 'codex-cli');
이 줄은 CLI가 로컬 데이터를 찾는 기준을 별도 하위 폴더로 옮겨요. 적용 범위는 Windows에서 전용 로그인 폴더를 사용하는 Codex 자식 프로세스로 한정됐어요. PC 전체 환경 변수나 사람이 쓰는 앱의 데이터 위치를 바꾼 것이 아니라, 예약 작업이 실행하는 CLI에 건네는 환경을 바꾼 거예요.
2026년 10월 7일 23:13:29 KST, 커밋 afe527e의 메시지는 “자동화 CLI의 앱 runtime 충돌을 분리하고 정상 환경에서 복구를 재개”예요. 충돌하는 데이터 경로를 분리하고, 정상 실행이 확인되면 멈춰 있던 복구 절차를 이어 가도록 바꿨다는 기록이에요.
앱과 CLI가 쓰는 실행 파일, 전용 로그인, 앱과 공유하는 샌드박스 폴더는 유지했어요. 이미 함께 쓰도록 맞춘 설정 전체를 다시 나누기보다, 현재 오류가 가리킨 런타임 데이터의 위치만 갈랐어요. 흐름을 짧게 쓰면 이랬어요.
예약 CLI 시작 → 앱 런타임 파일 검사 → 실행 중인 파일의 ACL 갱신 시도 → 파일 사용 충돌 → 샌드박스 시작 실패
수정 뒤에는 CLI에 별도 로컬 데이터 기준을 넘겨 그 앱 런타임 경로로 들어가던 충돌을 피했어요. 이 사건에서 확인한 원인은 이 PC의 실행 로그와 코드 경로를 맞춰 본 결과예요. 모든 코덱스 시작 오류에 같은 처방을 붙일 근거로 넓히지는 않았어요.
명령 하나의 성공과 글 한 편의 공개를 따로 봤어요
수정 후 첫 확인은 샌드박스에서 명령이 시작되는지였어요. 결과에는 RECOVERY_SANDBOX_READY가 출력됐고 종료 코드는 0이었어요. 준비 상태를 알리는 정해진 문구가 나왔고, 명령도 정상 종료했다는 두 가지를 함께 본 거예요. 실제 모델이 PowerShell과 Node를 실행한 확인에서는 RECOVERY_AGENT_READY와 종료 0이 남았어요. 샌드박스 명령만 되는 상태와 에이전트가 도구로 명령을 실행하는 상태를 따로 확인했어요.
충돌을 풀다가 제한까지 풀린 것은 아닌지도 봤어요. 작업본 밖 홈 디렉터리에 새 파일을 쓰려는 시도는 EPERM으로 막혔고 파일이 생기지 않았어요. 호스트에서 응답하던 외부 URL도 샌드박스에서는 fetch failed로 끝났어요. 전자는 쓰기 권한 거부, 후자는 외부 인터넷 접근 실패를 확인한 기록이에요. 로컬 루프백 접근은 가능해서, 같은 PC 안의 통신과 외부 인터넷 차단을 구분했어요.
검사 숫자도 실행한 범위와 함께 읽었어요. 호스트에서는 관련 회귀 검사 19개가 통과했어요. 회귀 검사는 변경 때문에 기존 동작이 깨지지 않았는지 다시 보는 검사예요. 실제 샌드박스에서는 운영 조치 관련 2개, 환경 재개·수정 관문·문제별 피드백 관련 3개가 통과했어요. 다만 처음에 두 테스트 파일 전체를 샌드박스에서 돌린 실행은 여러 항목을 통과한 뒤 60초 제한으로 끝났어요. 이후 관련 항목을 분리해 통과를 확인한 것이고, 전체 테스트의 샌드박스 실행까지 성공한 것으로 세지 않았어요.
그다음 제가 “진짜 이상 없는지 대기건도 다 해봐”라고 요청했어요. 에이전트는 다음 예약 시각을 기다리지 않고, 확인이 밀려 있던 실제 작업 세 건을 기존 실행기로 직접 돌렸어요. 알림 메일은 보내지 않는 수동 실행이었고, 그중 글쓰기와 이어지는 두 건의 결과를 아래에 적어요. 뉴스 점검 20261007231405-news는 오후 11시 14분에 명령과 검색을 정상 실행했지만, 새 취재 조건을 충족하는 계획이 없어 편집상 보류로 끝났어요. 이 보류는 글을 낼 소재가 부족했다는 판단이에요. 환경 때문에 자료를 읽지도 못했던 오후 8시·10시의 실패와는 결과가 달랐어요.
GitHub 탐구 20261007231452-github는 실제 작성과 이미지 생성, 사이트 검사, Claude 최종 검수, 재검사를 이어 갔어요. 그리고 23:29:33 KST에 OpenHands Agent Canvas 글의 배포를 마쳤어요. 공개 주소의 응답은 HTTP 200이었고, 데스크톱과 390픽셀 모바일 화면에서 제목·본문·이미지를 확인했어요. 이 결과는 글을 쓰는 첫 명령부터 공개까지 이어지는 경로가 실제로 다시 작동했다는 증거예요.
마지막으로 오후 11시 31분 운영 점검 20261007233128-health는 35개 항목 모두 정상, 미해결 복구 목록 0건으로 남았어요. 이전 실패 이력은 보존한 채 현재 상태를 확인했어요.
다시 충돌하면 확인할 경로
앱과 CLI의 버전을 맞춰도, CLI가 실행 중인 앱 파일까지 검사하면 충돌할 수 있었어요. 이번에는 로그인과 공용 샌드박스 설정을 유지하고 CLI 자식 프로세스의 로컬 데이터 위치만 나눴어요. 모든 시작 오류에 같은 환경 변수 변경을 적용할 근거는 없어요.
오류가 돌아오면 현재 로그의 파일 이름과 앱·CLI가 보는 데이터 위치를 먼저 대조해야 해요. 과거 실패만 보고 정상으로 돌아온 환경에 같은 재시작 요청을 반복하지 않도록 했어요.
자기 Windows 작업 환경에서는 우선 아래 PowerShell 명령으로 현재 프로세스가 보는 데이터 위치와 실행 파일 후보를 살펴볼 수 있어요.
$env:LOCALAPPDATA
Get-Command codex -All | Select-Object Source
첫 줄은 지금 셸에 전달된 데이터 기준을, 둘째 줄은 그 셸에서 찾는 Codex 실행 파일 후보를 보여 줘요. 예약 실행기가 환경을 따로 넘긴다면 자식 프로세스 안에서도 같은 값을 확인해 비교해요. 실행 중인 파일을 찾을 때는 Windows 리소스 모니터의 연결된 핸들 검색에 오류에 나온 파일 이름을 넣어 볼 수 있어요. 확인한 경로에 개인 정보가 섞여 있다면 공개 기록에는 그 부분을 덜어 내요.
재시작 뒤에도 같은 파일에서 멈췄던 예약 작업은 데이터 위치를 나눈 뒤 자료 읽기부터 실제 글 공개까지 다시 이어졌어요. 따로 띄웠다는 말만으로는 부족했고, 실행 중에 어느 파일을 읽고 바꾸는지까지 봐야 했어요.