BigPizzaV3/CodexPlusPlus · error

API 未能生成本批整理结果

Error message

API 未能生成本批整理结果

What it means

completionText() throws this when the API refused to generate content: choices[0].finish_reason is 'content_filter' or choices[0].message.refusal is set. The provider's safety layer blocked the response, so no batch result exists to merge.

Solutions

  1. Try a different model or provider whose content filter is less restrictive for your content
  2. Review the conversation batch for content that may trip moderation and split it differently
  3. Check message.refusal text in the raw response to understand what was objected to
  4. If a self-hosted/compatible server is behind a moderation proxy, disable or loosen that layer

Example fix

// before
const text = completionText(body); // throws on refusal
// after
let text;
try { text = completionText(body); }
catch (e) {
  console.warn('refusal:', body?.choices?.[0]?.message?.refusal);
  text = await retryWithDifferentModel(messages);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const c = body?.choices?.[0];
if (c?.finish_reason === 'content_filter' || c?.message?.refusal) console.warn('provider refused:', c?.message?.refusal);

Try / catch

try { text = completionText(body); }
catch (e) {
  if (String(e).includes('未能生成')) {
    log.warn('refusal:', body?.choices?.[0]?.message?.refusal);
    return fallbackModelOrNotifyUser();
  }
  throw e;
}

Prevention

When it happens

Trigger: finish_reason==='content_filter' or a truthy message.refusal in the response body; commonly triggered by conversation content the provider's moderation flagged, or an over-restrictive safety system on the provider side.

Common situations: Conversations containing flagged content sent to strict providers; a safety system/proxy misfiring on benign technical content; using a provider whose content filter is aggressive (some regional endpoints); system prompt interpreted as policy-violating.

Related errors


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

Appendix: source

Thrown at tools/conversation-canvas/external-api.mjs:79

      size+=part.value.byteLength;if(size>4*1024*1024)throw Object.assign(Error('API 响应超过大小限制'),{canvasApiLocal:true});
      const text=decoder.decode(part.value,{stream:true});
      if(!sse){raw+=text;if(/^\s*(data:|:)/.test(raw)){sse=true;buffer=raw;raw='';}}
      else buffer+=text;
      if(sse){let end;while((end=buffer.indexOf('\n'))>=0){consume(buffer.slice(0,end).replace(/\r$/,''));buffer=buffer.slice(end+1);}}
      progress({bytes:size,chars:content.length});if(done)break;
    }
    const tail=decoder.decode();if(sse){buffer+=tail;if(buffer.trim())consume(buffer.replace(/\r$/,''));
      if(!done&&!finish)throw Object.assign(Error('API 流式连接提前结束,本批未提交'),{canvasApiLocal:true,retryable:true});
      return {choices:[{finish_reason:finish,message:{content}}]};
    }
    try{return JSON.parse(raw+tail);}catch{throw Object.assign(Error('API 返回的不是 JSON,请检查 API 地址'),{canvasApiLocal:true});}
  }finally{void reader.cancel().catch(()=>{});try{reader.releaseLock();}catch{}}
}

export function completionText(body){
  const choice=body?.choices?.[0];
  if(choice?.finish_reason==='length')throw Error('API 输出达到长度上限,本批未提交;请使用输出容量更大的模型');
  if(choice?.finish_reason==='content_filter'||choice?.message?.refusal)throw Error('API 未能生成本批整理结果');
  const content=choice?.message?.content;
  const value=typeof content==='string'?content:Array.isArray(content)?content.filter(p=>p?.type==='text').map(p=>p.text||'').join(''):'';
  if(!value.trim())throw Error('API 未返回有效的 choices[0].message.content,请确认兼容 Chat Completions');
  return value;
}

export function waitForApi(promise,signal){
  signal?.throwIfAborted();
  if(!signal)return promise;
  return new Promise((resolve,reject)=>{
    const abort=()=>{signal.removeEventListener('abort',abort);reject(signal.reason||new DOMException('Aborted','AbortError'));};
    signal.addEventListener('abort',abort,{once:true});
    promise.then(value=>{signal.removeEventListener('abort',abort);resolve(value);},error=>{signal.removeEventListener('abort',abort);reject(error);});
  });
}

export function createApiOrganizer({request,timeoutMs=300000,maxRetries=2,retryDelayMs=2000}){
  const requests=new Map();

View on GitHub (pinned to b1ed92e5e4)