{"record":{"id":"3b6df15945fbd7a9","repo":"chenhg5/cc-connect","slug":"codex-app-server-turn-start-returned-empty-turn-id","errorCode":null,"errorMessage":"codex app-server turn/start returned empty turn id","messagePattern":"codex app-server turn/start returned empty turn id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/codex/appserver_session.go","lineNumber":503,"sourceCode":"\t\t\"threadId\": threadID,\n\t\t\"input\":    input,\n\t}\n\tif model := s.GetModel(); model != \"\" {\n\t\tparams[\"model\"] = model\n\t}\n\tif effort := s.GetReasoningEffort(); effort != \"\" {\n\t\tparams[\"effort\"] = effort\n\t}\n\tif approval, _ := appServerModeSettings(s.mode); approval != \"\" {\n\t\tparams[\"approvalPolicy\"] = approval\n\t}\n\n\tvar resp turnStartResponse\n\tif err := s.request(\"turn/start\", params, &resp); err != nil {\n\t\treturn fmt.Errorf(\"codex app-server turn/start: %w\", err)\n\t}\n\tif resp.Turn.ID == \"\" {\n\t\treturn fmt.Errorf(\"codex app-server turn/start returned empty turn id\")\n\t}\n\n\ts.stateMu.Lock()\n\ts.currentTurn = resp.Turn.ID\n\ts.pendingMsgs = s.pendingMsgs[:0]\n\ts.stateMu.Unlock()\n\n\treturn nil\n}\n\nfunc (s *appServerSession) stageImages(prompt string, images []core.ImageAttachment) (string, []string, error) {\n\tif len(images) == 0 {\n\t\treturn prompt, nil, nil\n\t}\n\n\timgDir := filepath.Join(s.workDir, \".cc-connect\", \"images\")\n\tif err := os.MkdirAll(imgDir, 0o755); err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"codex app-server: create image dir: %w\", err)","sourceCodeStart":485,"sourceCodeEnd":521,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/codex/appserver_session.go#L485-L521","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ntype turnStartResponse struct {\n    Turn struct { ID string `json:\"id\"` } `json:\"turn\"`\n}\n// after — log unexpected payloads for diagnosis\nvar raw json.RawMessage\nreq(\"turn/start\", params, &raw)\nif err := json.Unmarshal(raw, &resp); err != nil || resp.Turn.ID == \"\" {\n    slog.Warn(\"codex turn/start unexpected payload\", \"payload\", string(raw))\n}","handlingStrategy":"validation","validationCode":"raw, err := requestRaw(\"turn/start\", params)\nif err != nil { return err }\nvar resp turnStartResponse\nif err := json.Unmarshal(raw, &resp); err != nil || resp.Turn.ID == \"\" {\n    slog.Warn(\"unexpected turn/start payload\", \"payload\", string(raw))\n    return fmt.Errorf(\"turn/start: empty turn id\")\n}","typeGuard":"func validTurnStart(resp turnStartResponse) bool { return resp.Turn.ID != \"\" }","tryCatchPattern":null,"preventionTips":["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"],"tags":["codex","app-server","protocol","empty-response"],"backgroundTag":"empty-api-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}