classifyPiAiError maps textual HTTP 5xx errors ("Internal Server Error") to non-retryable PI_AI_ERROR, skipping automatic retries #7691
Replies: 1 comment
你的诊断在代码里成立——而且你能指出确切那一行我按你给的环境( function classifyPiAiError(message: string): string {
...
if (/\b5\d\d\b/.test(message)) return 'SERVER' // :50
...
return 'PI_AI_ERROR' // :67
}
所以你描述的三步复合链条(pi-ai 丢状态数字 → 我们只按数字识别 5xx → 兜底码不在白名单)在代码上完全成立。 一条你可能没看到的旁证(对你有利)同一个文件的
也就是说:状态数字丢失不是我们刻意为之,而是上游把错误拍平了,我们这边只能靠字符串。这正好支持你建议 (2)(让 pi-ai 保留 对你建议 (1) 的补充你的正则
关于你的 workaround,我核了配置形状,是对的
我唯一想提醒的措辞你写" (另:你的严重性评估"medium-low、无数据丢失"我同意。补充一条影响面——任何走自定义网关、且有 5xx 返回纯文本的服务都会踩到,而这恰好是本仓推荐的自定义 provider 用法。) |
Uh oh!
There was an error while loading. Please reload this page.
Filing a bug report here per the README ("Submit feedback or bug reports through GitHub Discussions"). It concerns the error-classification layer of
packages/llm/llm-pi-ai, which degrades the retry behavior ofdsh-llm-retryfor transient upstream 5xx failures.Summary
When an upstream gateway answers a request with HTTP 500 and a plain-text body like
Internal Server Error, the failure is classified asPI_AI_ERRORinstead ofSERVER. SincePI_AI_ERRORis not inDEFAULT_RETRYABLE_CODES,dsh-llm-retrynever retries it, and a transient 5xx that would self-heal under theSERVERcode is surfaced to the user as a terminal "round failed" error banner.Environment
@deepseek-ai/dsh0.1.7-rc.1@deepseek-ai/dsh-llm-pi-ai0.1.7-rc.1@earendil-works/pi-ai0.85.1providersentries (api: openai-completions/openai-responses) behind gateways that return bare-text 5xx bodies.What happens
HTTP 500with bodyInternal Server Error(the default Express/Koa error text; no status digits in the body).本轮运行失败 Internal Server Errorwith error codePI_AI_ERROR.retryPolicyismode: normal.Root cause (three compounding steps)
pi-ai drops the status digits on some paths.
utils/error-body.js→formatProviderErrorcomposes three display shapes. On themessageCarriesBodyhappy path (SDK already folded the body intoerror.message) it returnsnorm.messageunchanged — e.g. the bare stringInternal Server Error— without a<status>:prefix, so no5xxdigits survive into the message DSH classifies.classifyPiAiErroronly detects 5xx by digits. Inpackages/llm/llm-pi-ai/lib/index.js:A message consisting of the words
Internal Server Errorcontains no digits, so theSERVERbranch never matches and the error falls through to thePI_AI_ERRORcatch-all. The same applies toBad Gateway,Service Unavailable,Gateway Timeout, etc.The catch-all code is not retryable.
packages/llm/llm/lib/types/retry-policy.js:PI_AI_ERRORis absent, sodsh-llm-retry(!policy.retryableCodes.includes(failure.code)) stops immediately — a transient upstream 5xx becomes a user-visible failure with zero retries.Impact
PI_AI_ERRORlabel is misleading during triage (it reads like a pi-ai internal fault while the real cause is an upstream 5xx).Suggested fix (either or both)
In
classifyPiAiError, match standard HTTP status phrases:(Optionally add
/\b4\d\d\b|bad request/coverage forINVALID_REQUESTin the same style.)In pi-ai's
formatProviderError, keep the status digits on themessageCarriesBodypath (e.g.500: Internal Server Error) so downstream classifiers always have the numeric status available.Optionally, consider whitelisting
PI_AI_ERRORinDEFAULT_RETRYABLE_CODES— though classifying correctly (fix 1) is the more precise remedy.Workaround (users, current version)
Add the catch-all code to the route's
retryableCodesin the profile config (explicit lists replace the default, so include the defaults):Evidence
本轮运行失败 Internal Server Error— codePI_AI_ERROR, message with zero digits.dsh-llm-pi-ai/lib/index.jsclassifyPiAiError(/\b5\d\d\b/digit heuristic, catch-allreturn "PI_AI_ERROR").dsh-llm/lib/types/retry-policy.jsDEFAULT_RETRYABLE_CODES.@earendil-works/pi-ai/dist/utils/error-body.jsformatProviderError/messageCarriesBody.All reactions