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

  1. Update canvas.user.js to the release matching your Codex version.
  2. Check loadNativeRuntime() output in devtools to find the new export names and update the bindings.
  3. Pin/rollback the Codex app version to one compatible with the current script.
  4. 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

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


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)