NanoClaw의 Codex 제공자에서 "Turn timed out after 600000ms"가 표시된다는 것은 Codex의 Responses WebSocket이 이벤트 전송을 중단했고, Codex가 보고하는 첫 오류는 NanoClaw의 고정된 10분 턴 타이머가 이미 발동한 뒤에야 도착한다는 뜻입니다. Codex는 유휴 스트림을 5분 동안 기다리고, 처음에는 조용히 재시도하며, 두 번째 재시도에서만 보고합니다. 이 현상은 2026년 10월 1일, 버그 보고서에 기재된 버전인 Codex 0.138.0과 NanoClaw가 현재 고정하는 버전인 0.155.1에서 재현했습니다. 연결을 수락한 뒤 조용해지는 로컬 엔드포인트를 대상으로 했습니다.
이는 확인 당시 maintainer의 답변 없이 열려 있던 NanoClaw 이슈 #3338의 재현 사례입니다. provider 자체의 선택 사항은 NanoClaw API 비용 및 provider를 참조하세요. 다른 이유로 Telegram에서 봇이 응답하지 않는 경우에는 NanoClaw Telegram이 응답하지 않음을 확인하세요.
표시되는 내용
봇에 메시지를 보내도 10분 동안 답변이 없습니다 — 오류도, 부분 응답도 없습니다 — 그러고 나서 오류가 발생합니다. 그동안 보낸 메시지는 사라지는 것처럼 보입니다. 로그에는 다음과 같이 표시됩니다:
# Codex, inside the agent container (from the issue; our 0.155.1 run printed the same reason)
stream error: idle timeout waiting for websocket
stream disconnected - retrying sampling request (1/5 ...)
# NanoClaw, ten minutes after the message
Error: Turn timed out after 600000ms재현에서 확인된 내용
| 턴 시작 후 경과 시간(초) | Codex 0.138.0 | Codex 0.155.1(NanoClaw 고정 버전) |
|---|---|---|
| ≈1–16 | 첫 요청을 WebSocket으로 전송; 응답 없음 | 16초에 Codex가 종료한 워밍업 요청; 실제 요청이 이어짐 |
| ≈300–316 | 첫 요청 시간 초과; Codex가 턴의 요청을 다시 전송 — 알림 없음 | 유휴 시간 초과; 재시도 1이 로그에 기록됨 — 알림 없음 |
| 600 | NanoClaw의 타이머가 턴을 종료: "Turn timed out after 600000ms" | |
| ≈601–616 | 유휴 시간 초과; "retrying sampling request (1/5)"가 로그에 기록됨 — 여전히 알림 없음(750초 동안 전혀 없음) | 첫 error 알림: "Reconnecting... 2/5", "idle timeout waiting for websocket" — 15초 늦음 |
엔드포인트는 WebSocket을 수락하고 아무것도 보내지 않는 대체 구현이었고, 클라이언트는 실제 Codex app-server 바이너리였습니다. NanoClaw가 사용하는 것과 동일한 호출로 구동했으며 NanoClaw 자체의 규칙으로 판단했습니다 — 오류 알림이 발생하거나 턴이 완료되면 종료하고, 그렇지 않으면 10분 타이머가 종료합니다.
타이밍이 이처럼 크게 어긋나는 이유
- Responses 스트림의 다음 이벤트를 기다릴 때 Codex는 300초 동안 기다립니다. 그 시간이 지나면 스트림이 끊긴 것으로 판단하고 최대 5회 재시도합니다. 이는 두 버전 모두에서 내장 OpenAI 제공자의 기본값입니다.
- Codex는 첫 번째 WebSocket 재시도를 숨깁니다. 재시도 코드의 주석에 "In release builds, hide the first websocket retry notification to reduce noisy transient reconnect messages"라고 명시되어 있습니다. 따라서 프런트엔드는 두 번째 재시도가 될 때까지, 대략 10분 동안 아무것도 받지 못합니다.
- NanoClaw는 Codex 턴에 정확히 600,000ms를 부여합니다 그리고 모든 오류 알림에서 턴을 종료합니다. 두 번의 유휴 구간과 Codex의 워밍업을 합치면 이보다 길기 때문에, 기본 설정에서는 타이머가 먼저 승리합니다.
침묵하는 동안 보낸 메시지는 유실됩니다
NanoClaw는 활성 턴 중 도착한 후속 메시지를 Codex의 turn/steer로 보냅니다. 테스트에서 Codex는 이를 멈춘 턴에 수락했지만 업스트림으로 새로운 내용을 보내지 않았습니다. 시간 초과 후 스레드를 재개하자 원래 프롬프트는 기록에 있었지만 steer된 메시지는 없었습니다. 이 때문에 봇이 완전히 멈춘 것처럼 보입니다. 입력하는 모든 내용이 절대 끝나지 않을 턴으로 들어가기 때문입니다.
복구 방법
확인됨: 시간 초과 후 다음 메시지는 새 app-server에서 같은 스레드를 재개합니다 — NanoClaw가 수행하는 동작입니다 — 그리고 테스트에서는 엔드포인트가 응답하자 0.16초 만에 완료되었습니다. 따라서 시간 초과를 기다린 다음 침묵하는 동안 입력한 내용을 다시 보내세요. 다음 메시지도 멈춘다면 업스트림 연결이 여전히 문제입니다 — OpenAI의 상태를 확인하고, 컨테이너가 proxy를 통해 OpenAI에 연결된다면 해당 proxy가 WebSocket 업그레이드를 전달하는지 확인하세요(풀 리퀘스트 #2672는 그렇지 않은 proxy를 설명하지만, 여기서는 테스트하지 않았습니다).
대기 시간을 줄이는 옵션(측정했지만 출시되지 않음):
| 변경 사항 | NanoClaw가 처음 확인하는 오류 | 대응 |
|---|---|---|
| WebSockets 대신 HTTP/SSE를 통한 Codex — PR #3851, 오픈 상태, 미병합 | 300초: "Reconnecting... 1/5" | 알림에는 Codex가 재시도한다고 표시되고 실제로 재시도했지만, NanoClaw는 이를 수신하면 턴을 종료합니다 — 10분이 아닌 5분 만에 실패 |
WebSockets에서 Codex stream_idle_timeout_ms = 60,000 | 136초 | NanoClaw가 Codex의 config.toml을 직접 작성하므로, 이는 추가할 수 있는 설정이 아니라 코드 변경입니다 |
두 경우 모두 여전히 턴을 실패로 종료합니다. 측정 결과가 가리키는 것은 NanoClaw 측의 문제입니다 — 재시도 중인 오류를 종료가 아닌 진행으로 처리하거나, 마지막 실제 이벤트부터 측정한 턴 예산을 사용하는 방식입니다. 하지만 이는 이러한 테스트에서 도출한 추론일 뿐이며, maintainer가 확인한 수정 사항은 아닙니다.
자주 묻는 질문
NanoClaw의 Codex 에이전트가 10분 동안 아무 반응이 없다가 시간 초과되는 이유는 무엇인가요?
Codex가 보고하려는 첫 번째 문제 신호가 NanoClaw가 이미 포기한 뒤에 도착하기 때문입니다. Responses WebSocket이 이벤트 전송을 멈추면 Codex는 300초의 유휴 시간 초과를 기다린 뒤 재시도하지만, 릴리스 빌드에서는 의도적으로 첫 번째 WebSocket 재시도를 숨깁니다. 첫 번째 오류 알림은 두 번째 300초 창이 지난 뒤에야 도착하며, 이는 NanoClaw의 고정된 600,000ms 턴 타이머보다 늦습니다. 2026년 10월 1일 재현한 결과, Codex 0.138.0은 750초 동안 알림을 전혀 보내지 않았고 NanoClaw의 현재 고정 버전인 0.155.1은 615.7초에 첫 알림을 보냈습니다. NanoClaw의 규칙은 600초에 턴을 종료했습니다.
봇이 멈춘 동안 보낸 메시지는 어떻게 되나요?
멈춘 턴으로 흘러들어가고, 저희가 실행한 테스트에서는 유실됩니다. NanoClaw는 활성 턴 중 도착한 후속 메시지를 Codex의 턴/steer로 라우팅합니다. Codex는 이를 같은 멈춘 턴에 수락했고, 업스트림으로 새로운 내용을 보내지 않았으며, 시간 초과 후 재개했을 때 steer된 텍스트는 스레드 기록에 없었습니다. 봇이 다시 응답하면 침묵하는 동안 입력한 내용은 무엇이든 다시 보내세요.
NanoClaw Codex 턴 시간 초과에서 어떻게 복구하나요?
다음 메시지를 보내세요. NanoClaw는 새 app-server를 시작하고 같은 스레드를 재개합니다. 저희가 실행한 테스트에서는 엔드포인트가 응답하자 재개된 턴이 0.16초 만에 완료되었고, 원래 프롬프트는 여전히 기록에 있었습니다. 업스트림이 계속 지연 중이면 새 턴도 같은 방식으로 멈추므로, 먼저 OpenAI의 상태 또는 네트워크 경로를 확인하세요 — 그리고 삼켜진 후속 메시지는 다시 보내세요.
Codex를 WebSockets 대신 HTTP로 전환하면 문제가 해결되나요?
침묵 시간이 짧아지지만, 아직 병합되지 않았습니다. 풀 리퀘스트 #3851(이슈 제보자가 올린 오픈 상태)은 NANOCLAW_CODEX_TRANSPORT=http를 추가하여 Codex를 HTTP/SSE로 라우팅합니다. 저희가 그 형태로 실행한 테스트에서는 첫 오류 알림이 타이머 이후가 아니라 300초에 도착했지만 — Codex가 이미 재전송 중인데도 willRetry true를 포함했고, NanoClaw는 모든 오류 알림에서 턴을 종료하므로, 턴은 복구되는 대신 5분에 실패합니다.
이것은 일반적인 Codex 시간 초과인가요?
아닙니다. Codex 자체는 계속 재시도합니다. 10분의 침묵은 NanoClaw의 턴 타이머가 Codex의 숨겨진 첫 번째 재시도와 맞물리는 방식에서 발생합니다. 0.155.1에서는 app-server가 약 616초에 "Reconnecting... 2/5"를 보고했습니다 — 턴 예산이 더 긴 클라이언트라면 이를 볼 수 있지만, NanoClaw에서는 절대 보지 못합니다. NanoClaw의 기본 제공자는 Claude Agent SDK이며, 이 문제에는 관여하지 않습니다.
2026년 10월 1일, npm의 native codex app-server 바이너리(@openai/codex 0.138.0 및 0.155.1, darwin-arm64)를 사용해 재현했습니다. NanoClaw의 providers 브랜치(commit 3959d1f055)에 있는 initialize, thread/start, turn/start 및 turn/steer 매개변수를 사용해 stdio를 통해 구동했으며, WebSocket 및 HTTP/SSE를 통한 로컬 Responses 대체 구현을 대상으로 했습니다. Codex 기본값과 숨겨진 첫 재시도는 codex-rs의 rust-v0.138.0 및 rust-v0.155.1 태그에서 확인했습니다. 실행하지 않은 항목: NanoClaw의 orchestrator 및 container, OneCLI, Telegram 또는 OpenAI의 실제 엔드포인트 — 대체 구현은 조용한 스트림을 재현할 뿐, OpenAI의 스트림이 조용해진 원인은 재현하지 않습니다. 이슈 및 풀 리퀘스트 상태는 같은 날 확인했습니다.