chenhg5/cc-connect · error
codex app-server turn/start returned empty turn id
Error message
codex app-server turn/start returned empty turn id
What it means
The turn/start request succeeded (no transport error) but the response contained no turn id (resp.Turn.ID == ""). The library requires a turn id to track the current turn, correlate events, and support interruption, so an empty id is treated as an unusable response.
Source
Thrown at agent/codex/appserver_session.go:503
"threadId": threadID,
"input": input,
}
if model := s.GetModel(); model != "" {
params["model"] = model
}
if effort := s.GetReasoningEffort(); effort != "" {
params["effort"] = effort
}
if approval, _ := appServerModeSettings(s.mode); approval != "" {
params["approvalPolicy"] = approval
}
var resp turnStartResponse
if err := s.request("turn/start", params, &resp); err != nil {
return fmt.Errorf("codex app-server turn/start: %w", err)
}
if resp.Turn.ID == "" {
return fmt.Errorf("codex app-server turn/start returned empty turn id")
}
s.stateMu.Lock()
s.currentTurn = resp.Turn.ID
s.pendingMsgs = s.pendingMsgs[:0]
s.stateMu.Unlock()
return nil
}
func (s *appServerSession) stageImages(prompt string, images []core.ImageAttachment) (string, []string, error) {
if len(images) == 0 {
return prompt, nil, nil
}
imgDir := filepath.Join(s.workDir, ".cc-connect", "images")
if err := os.MkdirAll(imgDir, 0o755); err != nil {
return "", nil, fmt.Errorf("codex app-server: create image dir: %w", err)View on GitHub (pinned to 4000b2338a)
Solutions
- Compare turnStartResponse field tags against the codex app-server protocol version in use and upgrade codex
- Log the raw turn/start JSON to confirm the shape the server actually returns
- Check codex server logs for why the turn failed to get an id
- Update the response struct / parser to the current protocol schema
Example fix
// before
type turnStartResponse struct {
Turn struct { ID string `json:"id"` } `json:"turn"`
}
// after — log unexpected payloads for diagnosis
var raw json.RawMessage
req("turn/start", params, &raw)
if err := json.Unmarshal(raw, &resp); err != nil || resp.Turn.ID == "" {
slog.Warn("codex turn/start unexpected payload", "payload", string(raw))
} Defensive patterns
Strategy: validation
Validate before calling
raw, err := requestRaw("turn/start", params)
if err != nil { return err }
var resp turnStartResponse
if err := json.Unmarshal(raw, &resp); err != nil || resp.Turn.ID == "" {
slog.Warn("unexpected turn/start payload", "payload", string(raw))
return fmt.Errorf("turn/start: empty turn id")
} Type guard
func validTurnStart(resp turnStartResponse) bool { return resp.Turn.ID != "" } Prevention
- Pin the codex CLI version and test protocol compatibility after upgrades
- Log raw JSON-RPC payloads at debug level to detect schema drift early
- Add round-trip tests that assert resp.Turn.ID != "" against a stub server
When it happens
Trigger: The codex app-server returned a 200-style JSON-RPC result whose turn object is missing or has an empty id — typically a protocol/version mismatch or a server-side turn that failed to schedule.
Common situations: Running a codex binary whose turn/start response schema differs (older/newer protocol); the server accepted the request but silently dropped the turn; the turnStartResponse struct fields no longer match the server's JSON keys.
Understand the failure class
Background: "empty response", "returned no data", "empty embeddings": what HTTP 200-with-empty-body errors mean across libraries — this error's family across 36 libraries.
Related errors
- codex app-server thread id is empty
- codex app-server turn/start: %w
- turn failed (no details)
- %s
- codex app-server connection is closed
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/3b6df15945fbd7a9.
Report an issue: GitHub.