BigPizzaV3/CodexPlusPlus · error · Error
当前 Codex 版本的对话读取接口不兼容
Error message
当前 Codex 版本的对话读取接口不兼容
What it means
nativeContext() loads Codex's private runtime module and checks that the exported functions it binds (ESt, kr) are still functions before reading thread context from React fibers. If those minified exports are absent or not functions, the userscript cannot read history on this Codex build, so it throws.
Solutions
- Update canvas.user.js to the release matching your Codex version.
- Check loadNativeRuntime() output in devtools to find the new export names and update the bindings.
- Pin/rollback the Codex app version to one compatible with the current script.
- Add a version probe that maps multiple candidate export names so the script survives minor renames.
Example fix
// before
if(typeof native.ESt!=='function'||typeof native.kr!=='function')throw Error('当前 Codex 版本的对话读取接口不兼容');
// after
const kr=native.kr??native.newerKrName;
if(typeof native.ESt!=='function'||typeof kr!=='function')throw Error('当前 Codex 版本的对话读取接口不兼容'); Defensive patterns
Strategy: try-catch
Validate before calling
const native=await loadNativeRuntime(); if(typeof native?.ESt!=='function'||typeof native?.kr!=='function')return null;
Type guard
const hasNativeReadApi=n=>n!=null&&typeof n.ESt==='function'&&typeof n.kr==='function';
Try / catch
try{return await nativeContext(threadId);}catch(e){if(e.message==='当前 Codex 版本的对话读取接口不兼容'){showUpdateNeededBanner();return null;}throw e;} Prevention
- Check the script/Codex compatibility matrix before updating Codex
- Probe candidate export names instead of one hard-coded symbol
- Disable history import when the runtime probe fails
- Subscribe to Codex release notes for internal API changes
When it happens
Trigger: Calling nativeContext(threadId) on a Codex version where loadNativeRuntime() returns a module without ESt/kr function exports — i.e. after a Codex release that renamed/removed those internals.
Common situations: Codex auto-updated and internal minified symbol names changed; running an old canvas.user.js against a new Codex build (or vice versa); runtime module failed to load partially.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- 当前 Codex 版本的空白侧边对话接口不兼容
- 侧边对话初始化未完成
- 本批整理中断,已完成批次已保存,可点击继续
- 上次提交结果尚未确认,请稍后点击继续
- 后台整理会话已失效,已完成批次仍保留;点击继续可重新处理本批
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/d0f2368467f70af9.
Report an issue: GitHub.
Appendix: source
Thrown at tools/conversation-canvas/public/canvas.user.js:465
onProgress(`正在读取历史 ${index+1}/${turns.length} · 已读 ${parsed.length} 条消息…`);
if(turn.status==='completed'&&itemCursor!==null)await cache.set(`${key}:partial`,{messages:parsed,cursor:itemCursor});
}while(itemCursor!==null);
if(turn.status==='completed'){await cache.set(key,parsed);await cache.delete(`${key}:partial`);}
}
// Older completed-turn caches contain text and IDs but no turn metadata.
// The current directory and cache key provide the authoritative owner.
for(const message of parsed)if(!seen.has(message.id)){seen.add(message.id);messages.push({...message,turnId:turn.id,turnStatus:turn.status});}
}
// Bound cross-task retention, without truncating the returned conversation.
while(cache.size>500)cache.delete(cache.keys().next().value);
return messages;
}
// Runtime bindings are versioned separately. History reading does not require
// the private side-conversation submission API to be available.
async function nativeContext(threadId){
const native=await loadNativeRuntime();
if(typeof native.ESt!=='function'||typeof native.kr!=='function')throw Error('当前 Codex 版本的对话读取接口不兼容');
native.kr();
const scopes=[],callbacks=[],seeds=[],navigation=[];
const visited=new Set(),domVisited=new Set();
// ProseMirror owns the inner editable DOM. React's expando is on a wrapper,
// not necessarily on the contenteditable itself. Start from native surfaces
// too, so history reading remains available while the composer is hidden.
for(const element of document.querySelectorAll('[contenteditable],textarea,main,[data-testid="app-shell-header-context-menu-surface"]')){
for(let dom=element;dom&&!domVisited.has(dom);dom=dom.parentElement){
domVisited.add(dom);
for(const key of Object.keys(dom)){
if(key.startsWith('__reactFiber$'))seeds.push(dom[key]);
if(key.startsWith('__reactContainer$')){
const root=dom[key];seeds.push(root?.stateNode?.current??root);
}
}
}
}
const inspect=fiber=>{View on GitHub (pinned to b1ed92e5e4)