{"record":{"id":"038c318ebcc9f888","repo":"BigPizzaV3/CodexPlusPlus","slug":"native-history-038c31","errorCode":null,"errorMessage":"历史轮次接口返回格式无效","messagePattern":"历史轮次接口返回格式无效","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tools/conversation-canvas/native-history.mjs","lineNumber":17,"sourceCode":"import {parseMessage} from './model.mjs';\n\nexport function isExportSizeError(message){\n  return /超过.*(?:大小|限制)|too (?:large|big)|size.{0,30}limit|maximum.{0,20}size/i.test(String(message));\n}\n\n// Wire contracts verified in the installed Desktop's history loader:\n// thread/turns/list(itemsView=notLoaded), then thread/items/list per turn.\nexport async function readNativeHistory(send,threadId,cache=new Map(),onProgress=()=>{},signal){\n  const turns=[],turnIds=new Set(),turnCursors=new Set();\n  let cursor=null;\n  do{\n    signal?.throwIfAborted();\n    if(turnCursors.has(cursor))throw Error('历史轮次分页游标重复');\n    turnCursors.add(cursor);\n    const page=await send('thread/turns/list',{threadId,cursor,limit:20,itemsView:'notLoaded',sortDirection:'asc'});\n    if(!Array.isArray(page?.data))throw Error('历史轮次接口返回格式无效');\n    for(const turn of page.data){\n      if(typeof turn.id!=='string')throw Error('历史轮次缺少 ID');\n      if(!turnIds.has(turn.id)){turnIds.add(turn.id);turns.push(turn);}\n    }\n    cursor=page.nextCursor??null;\n    onProgress(`正在读取轮次目录 · ${turns.length} 轮…`);\n  }while(cursor!==null);\n  const messages=[],seen=new Set();\n  for(const [index,turn] of turns.entries()){\n    signal?.throwIfAborted();onProgress(`正在读取历史 ${index+1}/${turns.length}…`);\n    const key=`${threadId}:${turn.id}`,cached=await cache.get(key);\n    let parsed;\n    if(cached&&turn.status==='completed')parsed=cached;\n    else{\n      const partial=turn.status==='completed'?await cache.get(`${key}:partial`):null;\n      parsed=partial?.messages?.slice()||[];let itemCursor=partial?.cursor??null,limit=32;const cursors=new Set();\n      do{\n        signal?.throwIfAborted();","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/tools/conversation-canvas/native-history.mjs#L1-L35","documentation":"readNativeHistory expects each thread/turns/list page to be an object with an array data field. This throw means the response lacked page.data or it was not an array, indicating an unexpected response shape from the turns API.","triggerScenarios":"send('thread/turns/list') returns null/undefined, an error envelope, or a data field that is not an array.","commonSituations":"Wrong RPC method name or threadId for the installed codex version; backend returned an error object instead of a page; middleware mangling responses.","solutions":["Log the raw page response to see what the backend actually returned","Verify threadId is valid and the codex app/protocol version supports thread/turns/list with itemsView","Check that send() is wired to the real codex RPC channel, not a stale mock","Retry after restarting the codex backend"],"exampleFix":"// before\nconst page = await send('thread/turns/list', ...); // page undefined\n// after\nif (!page || !Array.isArray(page?.data)) throw new Error('turns page invalid: ' + JSON.stringify(page));","handlingStrategy":"type-guard","validationCode":"const page = await send('thread/turns/list', args);\nif (!page || !Array.isArray(page.data)) throw new Error('bad turns page');","typeGuard":"const isTurnsPage = p => Boolean(p) && Array.isArray(p.data);","tryCatchPattern":"try { await readNativeHistory(send, threadId) } catch (e) { if (e.message === '历史轮次接口返回格式无效') { inspectAndReconnect(); } else throw e; }","preventionTips":["Confirm the RPC method name matches your codex protocol version","Validate threadId before listing turns","Log raw responses when integrating new backends","Wrap send() to normalize error envelopes into pages"],"tags":["api","response-shape","pagination"],"backgroundTag":"unexpected-response-shape","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}