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.

Solutions

  1. Compare turnStartResponse field tags against the codex app-server protocol version in use and upgrade codex
  2. Log the raw turn/start JSON to confirm the shape the server actually returns
  3. Check codex server logs for why the turn failed to get an id
  4. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/3b6df15945fbd7a9. Report an issue: GitHub.

Appendix: 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)