BigPizzaV3/CodexPlusPlus · error

待恢复批次与资料不匹配

Error message

待恢复批次与资料不匹配

What it means

organizeLong resumes a possibly-persisted job. When a pending batch exists it maps pending.partIds through partIndex (current history parts); if any partId no longer resolves to a part, the saved batch no longer matches the conversation material and resuming is refused rather than producing corrupt output.

Solutions

  1. Restart organizeLong from the current history (drop the stale pending batch)
  2. Verify the saved document corresponds to the same thread/history snapshot
  3. Clear job.pending and let nextBatch recompute from done state if the parts changed
  4. Re-run from scratch; completed batches in state.done may still be reusable

Example fix

// before
await organizeLong(resumedDocument) // stale pending partIds
// after
resumedDocument.job.pending = null; // recompute batch from current parts
await organizeLong(resumedDocument)
Defensive patterns

Strategy: validation

Validate before calling

const idx = new Map(history.parts.map(p => [p.partId, p]));
const stale = (job.pending?.partIds || []).filter(id => !idx.has(id));
if (stale.length) throw new Error('stale pending parts: ' + stale.join(','));

Try / catch

try { await organizeLong(doc) } catch (e) { if (e.message === '待恢复批次与资料不匹配') { doc.job.pending = null; restartJob(); } else throw e; }

Prevention

When it happens

Trigger: Resuming a saved document whose job.pending.partIds reference parts that were removed/changed in history.parts (conversation edited or reloaded from a different history snapshot).

Common situations: Editing/deleting messages in the native thread while a long-organization run is paused; restoring an old checkpoint file against newer history; mismatched thread content between sessions.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/ef048726ec1c10f4. Report an issue: GitHub.

Appendix: source

Thrown at tools/conversation-canvas/long-organizer.mjs:126

// coverage; a restart can resume its pending side-chat turn before resubmitting.
export async function organizeLong({messages,record,save,run,signal,progress=()=>{},onGraph=()=>{},compact=false}){
  const history=await prepareHistory(messages,signal);
  const partIndex=new Map(history.parts.map(part=>[part.partId,part]));
  const previous=record?.published;
  let job=record?.job;
  if(!job||!compatible(job.state.manifest,history.manifest)){
    const incremental=previous?.done&&compatible(previous.manifest,history.manifest);
    const state=incremental?{...previous,manifest:history.manifest}:{schemaVersion:2,nodes:[],currentNodeId:null,batchCount:0,done:{},manifest:history.manifest};
    job={kind:incremental||!previous?.nodes?.length?'incremental':'rebuild',state,session:{},pending:null};
  }else job={...job,state:{...job.state,manifest:history.manifest}};
  let document={version:3,published:previous??null,job};
  const persist=async()=>{signal?.throwIfAborted();await save(structuredClone(document));};
  await persist();
  for(;;){
    signal?.throwIfAborted();
    // A resumed final batch must not absorb messages appended while paused.
    const batch=job.pending?job.pending.partIds.map(id=>partIndex.get(id)):nextBatch(history.parts,job.state.done);
    if(batch.some(part=>!part))throw Error('待恢复批次与资料不匹配');
    if(!batch.length)break;
    progress(`正在整理第 ${job.state.batchCount+1} 批 · 已处理 ${Object.keys(job.state.done).length}/${history.parts.length} 个片段`);
    if(!job.pending){job.pending={requestId:crypto.randomUUID(),partIds:batch.map(p=>p.partId),compact};await persist();}
    if(JSON.stringify(job.pending.partIds)!==JSON.stringify(batch.map(p=>p.partId)))throw Error('待恢复批次与资料不匹配');
    const {requestId}=job.pending,{catalog,prefix,aliases,prompt:basePrompt}=batchPrompt(batch,job.state,requestId,job.pending.compact);
    const repair=job.pending.repair;
    const missing=repair?.missingPartIds?.map(id=>aliases?Object.keys(aliases).find(key=>aliases[key]===id)||id:id);
    const prompt=basePrompt+(repair?`\n上次回复未通过覆盖校验,未保存任何本批节点。本次为第 ${repair.attempt}/2 次补正。上次漏引的 partId:${JSON.stringify(missing)}。请重新输出整个批次的完整 JSON,使用本次 requestId,覆盖本批全部 ${batch.length} 个片段(包括上次已引用的片段),不要只输出遗漏部分。发送前逐个核对 sources;重复讨论、进度说明也要归入有依据的任务节点。`: '');
    const response=await run(prompt,requestId,job.session,async()=>save(structuredClone(document)));
    signal?.throwIfAborted();
    let next;
    try{next=mergeBatch(response,job.state,batch,requestId,catalog,prefix,aliases);}
    catch(error){
      job.session.turnId=null;job.session.requestId=null;
      if(error.missingPartIds&&(repair?.attempt||0)<2){
        job.pending={requestId:crypto.randomUUID(),partIds:batch.map(p=>p.partId),compact:job.pending.compact,repair:{attempt:(repair?.attempt||0)+1,missingPartIds:error.missingPartIds}};
        await persist();progress(`本批遗漏 ${error.missingPartIds.length} 个片段,正在自动补正 ${job.pending.repair.attempt}/2…`);continue;
      }

View on GitHub (pinned to b1ed92e5e4)