BigPizzaV3/CodexPlusPlus · error · Error
历史轮次接口返回格式无效
Error message
历史轮次接口返回格式无效
What it means
readNativeHistory requires each thread/turns/list response to carry a data array of turns. This throw fires when the response is null/undefined or page.data is not an array, indicating the native API response does not match the expected thread/turns/list(itemsView=notLoaded) shape.
Solutions
- Update the plugin so its adapter mapping matches the installed Codex API schema
- Log the raw page response to identify the new envelope shape and adapt parsing
- Verify the threadId is valid and the thread still exists
- Retry the read — a transient RPC failure can return a null/empty response
Example fix
// before
if(!Array.isArray(page?.data))throw Error('历史轮次接口返回格式无效');
// after
const data=page?.data ?? page?.turns;
if(!Array.isArray(data))throw Object.assign(Error('历史轮次接口返回格式无效'),{cause:page}); Defensive patterns
Strategy: type-guard
Validate before calling
const page=await send('thread/turns/list',{threadId,cursor,limit:20,itemsView:'notLoaded',sortDirection:'asc'});
if(!page||typeof page!=='object'||!Array.isArray(page.data)) throw new Error('malformed turns page'); Type guard
function isTurnsPage(p){ return !!p && Array.isArray(p.data) && p.data.every(t=>typeof t?.id==='string'); } Try / catch
try { history=await readNativeHistory(send,threadId) } catch(e){ if(e.message==='历史轮次接口返回格式无效'){ logRawResponse(e.cause); retryOrAbort(); } else throw e; } Prevention
- Validate the response envelope before iterating page.data
- Keep the plugin adapter in sync with the installed Codex RPC schema
- Log the raw response object as error cause to catch protocol changes early
When it happens
Trigger: send('thread/turns/list',{...}) resolves with null, an error object, or an object whose data field is missing or not an array — e.g. backend returns {error:...}, or a protocol version changed the response envelope.
Common situations: Codex app updated with a changed RPC schema while the plugin adapter still expects the old shape; transient RPC failure surfaced as a null response; wrong threadId causing an error-shaped payload.
Related errors
- 历史轮次接口返回格式无效
- API 未能生成本批整理结果
- API 输出达到长度上限,本批未提交;请使用输出容量更大的模型
- API 未返回有效的 choices[0].message.content,请确认兼容 Chat Completions
- 历史轮次分页游标重复
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/665975d139df9b8f.
Report an issue: GitHub.
Appendix: source
Thrown at tools/conversation-canvas/public/canvas.user.js:406
}
const loadNativeRuntime=createNativeLoader();
function isExportSizeError(message){
return /超过.*(?:大小|限制)|too (?:large|big)|size.{0,30}limit|maximum.{0,20}size/i.test(String(message));
}
// Wire contracts verified in the installed Desktop's history loader:
// thread/turns/list(itemsView=notLoaded), then thread/items/list per turn.
async function readNativeHistory(send,threadId,cache=new Map(),onProgress=()=>{},signal){
const turns=[],turnIds=new Set(),turnCursors=new Set();
let cursor=null;
do{
signal?.throwIfAborted();
if(turnCursors.has(cursor))throw Error('历史轮次分页游标重复');
turnCursors.add(cursor);
const page=await send('thread/turns/list',{threadId,cursor,limit:20,itemsView:'notLoaded',sortDirection:'asc'});
if(!Array.isArray(page?.data))throw Error('历史轮次接口返回格式无效');
for(const turn of page.data){
if(typeof turn.id!=='string')throw Error('历史轮次缺少 ID');
if(!turnIds.has(turn.id)){turnIds.add(turn.id);turns.push(turn);}
}
cursor=page.nextCursor??null;
onProgress(`正在读取轮次目录 · ${turns.length} 轮…`);
}while(cursor!==null);
const messages=[],seen=new Set();
for(const [index,turn] of turns.entries()){
signal?.throwIfAborted();onProgress(`正在读取历史 ${index+1}/${turns.length}…`);
const key=`${threadId}:${turn.id}`,cached=await cache.get(key);
let parsed;
if(cached&&turn.status==='completed')parsed=cached;
else{
const partial=turn.status==='completed'?await cache.get(`${key}:partial`):null;
parsed=partial?.messages?.slice()||[];let itemCursor=partial?.cursor??null,limit=32;const cursors=new Set();
do{
signal?.throwIfAborted();View on GitHub (pinned to b1ed92e5e4)