{"record":{"id":"9597956b86b14401","repo":"BigPizzaV3/CodexPlusPlus","slug":"api-canvas-user","errorCode":null,"errorMessage":"API 输出达到长度上限，本批未提交；请使用输出容量更大的模型","messagePattern":"API 输出达到长度上限，本批未提交；请使用输出容量更大的模型","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"tools/conversation-canvas/public/canvas.user.js","lineNumber":697,"sourceCode":"      const part=await waitForApi(reader.read(),signal);if(part.done)break;\n      size+=part.value.byteLength;if(size>4*1024*1024)throw Object.assign(Error('API 响应超过大小限制'),{canvasApiLocal:true});\n      const text=decoder.decode(part.value,{stream:true});\n      if(!sse){raw+=text;if(/^\\s*(data:|:)/.test(raw)){sse=true;buffer=raw;raw='';}}\n      else buffer+=text;\n      if(sse){let end;while((end=buffer.indexOf('\\n'))>=0){consume(buffer.slice(0,end).replace(/\\r$/,''));buffer=buffer.slice(end+1);}}\n      progress({bytes:size,chars:content.length});if(done)break;\n    }\n    const tail=decoder.decode();if(sse){buffer+=tail;if(buffer.trim())consume(buffer.replace(/\\r$/,''));\n      if(!done&&!finish)throw Object.assign(Error('API 流式连接提前结束，本批未提交'),{canvasApiLocal:true,retryable:true});\n      return {choices:[{finish_reason:finish,message:{content}}]};\n    }\n    try{return JSON.parse(raw+tail);}catch{throw Object.assign(Error('API 返回的不是 JSON，请检查 API 地址'),{canvasApiLocal:true});}\n  }finally{void reader.cancel().catch(()=>{});try{reader.releaseLock();}catch{}}\n}\n\nfunction completionText(body){\n  const choice=body?.choices?.[0];\n  if(choice?.finish_reason==='length')throw Error('API 输出达到长度上限，本批未提交；请使用输出容量更大的模型');\n  if(choice?.finish_reason==='content_filter'||choice?.message?.refusal)throw Error('API 未能生成本批整理结果');\n  const content=choice?.message?.content;\n  const value=typeof content==='string'?content:Array.isArray(content)?content.filter(p=>p?.type==='text').map(p=>p.text||'').join(''):'';\n  if(!value.trim())throw Error('API 未返回有效的 choices[0].message.content，请确认兼容 Chat Completions');\n  return value;\n}\n\nfunction waitForApi(promise,signal){\n  signal?.throwIfAborted();\n  if(!signal)return promise;\n  return new Promise((resolve,reject)=>{\n    const abort=()=>{signal.removeEventListener('abort',abort);reject(signal.reason||new DOMException('Aborted','AbortError'));};\n    signal.addEventListener('abort',abort,{once:true});\n    promise.then(value=>{signal.removeEventListener('abort',abort);resolve(value);},error=>{signal.removeEventListener('abort',abort);reject(error);});\n  });\n}\n\nfunction createApiOrganizer({request,timeoutMs=300000,maxRetries=2,retryDelayMs=2000}){","sourceCodeStart":679,"sourceCodeEnd":715,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/tools/conversation-canvas/public/canvas.user.js#L679-L715","documentation":"completionText() inspects the Chat Completions response; finish_reason==='length' means the model hit its max output tokens, so the generated batch is incomplete. The script refuses to commit a truncated batch and asks the user to pick a model/output budget with larger capacity.","triggerScenarios":"An organize batch request returns body.choices[0].finish_reason==='length' — the model stopped because max_tokens/output limit was reached before finishing the JSON batch.","commonSituations":"Large batch of long messages with a small-output model (e.g. 4k output limit); user set a low max_tokens in provider dashboard; model tends to verbosity and truncates the JSON payload.","solutions":["Switch to a model with a larger output token limit","Reduce the batch size / number of messages per organize request so output fits","If provider-side max_tokens is configurable, raise it","Re-run the organize after changing the model; the batch was not committed"],"exampleFix":"// before\nmodel: 'gpt-3.5-turbo' // 4k output, truncates\n// after\nmodel: 'gpt-4o' // larger output capacity","handlingStrategy":"try-catch","validationCode":"// check after response: body?.choices?.[0]?.finish_reason !== 'length'","typeGuard":null,"tryCatchPattern":"try{ await organizeLong(...) }catch(e){ if(e.message.includes('长度上限')){ switchToLargerOutputModel(); retry(); } }","preventionTips":["Pick models with ≥16k output for large batches","Keep batches small","Raise provider max_tokens limits"],"tags":["api","truncation","token-limit"],"backgroundTag":"upstream-api-error","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}