[Bug] OpenAI 兼容 provider 的混合 SSL 上下文导致特定中转站稳定 ReadTimeout(APIConnectionError)
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 45/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- python
- Área
- backend, networking
Línea de trabajo
Start in astrbot/core/utils/network_utils.py, at create_proxy_client() and the _SYSTEM_SSL_CTX built by build_ssl_context_with_certifi(), and check how resolved_verify is passed to httpx.AsyncClient. Run the reproduce script from the issue against a relay that reproduces the hang, comparing the hybrid context with the default verify. Done means the maintainers have chosen between a per-provider SSL option and a ReadTimeout fallback, and the chosen path works with that relay.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
[Bug] OpenAI 兼容 provider 使用混合 SSL 上下文时,对特定中转站(78code)稳定 ReadTimeout → APIConnectionError
What happened / 发生了什么
在 AstrBot 中配置一个 OpenAI 兼容中转站(https://www.78code.cc/v1,Gemini 系模型)后,Dashboard 的「测试」按钮对所有模型都返回:
openai.APIConnectionError: Connection error.
日志里连续重试 5 次全部失败:
[v4.29.4] [sources.request_retry:68]: [OpenAI] Request failed with retryable error; retrying (2/5): APIConnectionError: Connection error.
[v4.29.4] [sources.request_retry:68]: [OpenAI] Request failed with retryable error; retrying (3/5): APIConnectionError: Connection error.
[v4.29.4] [sources.request_retry:68]: [OpenAI] Request failed with retryable error; retrying (4/5): APIConnectionError: Connection error.
[v4.29.4] [sources.request_retry:68]: [OpenAI] Request failed with retryable error; retrying (5/5): APIConnectionError: Connection error.
[v4.29.4] [utils.network_utils:86]: [OpenAI] Network connection failed (APIConnectionError): Connection error.
但同一个 key、同一个 URL、同样的请求体,用别的客户端发都能成功:
- PowerShell
Invoke-RestMethod:200,gemini-3-flash-preview约 1.9 秒返回 - AstrBot 自带 Python 里用
requests:200 - AstrBot 自带 Python 里用
openaiSDK + 默认 httpx client:200,正常返回内容
所以不是 key、不是网络、也不是上游慢。
根因定位 / Root cause
astrbot/core/utils/network_utils.py 的 create_proxy_client() 会给每个 OpenAI 系 provider 构造一个带「系统证书库 + certifi」混合 SSL 上下文的 httpx client:
# network_utils.py:120
resolved_verify = _SYSTEM_SSL_CTX if verify is None else verify # _SYSTEM_SSL_CTX = build_ssl_context_with_certifi()
...
return httpx_module.AsyncClient(verify=resolved_verify, headers=headers)
对这个中转站,只要把这个上下文传给 httpx.AsyncClient,请求就会挂住直到读超时;换成 httpx 默认校验(certifi)则 2 秒内返回 200。
对照实验(在 AstrBot 自带 Python 中运行,其余条件完全相同):
| http_client | 结果 |
|---|---|
httpx.AsyncClient(verify=build_ssl_context_with_certifi()) |
ReadTimeout(60 秒后超时),SDK 最终抛 APITimeoutError → APIConnectionError |
httpx.AsyncClient()(默认 certifi) |
200 OK,约 2 秒 |
补充一个反直觉的点:同一个混合上下文,用同步 httpx.get(verify=ctx) 请求同一地址是 200。所以问题只出现在「异步 httpx + 该上下文 + 该站点」的组合上,不是证书链缺失、也不是证书校验失败(没有 CERTIFICATE_VERIFY_FAILED,纯粹是连接建立后读不到响应)。 |
|
| 上下文本身看起来也正常: |
SSLContext | check_hostname=True | verify_mode=CERT_REQUIRED
min tls: TLSv1_2 | max tls: -1 | ALPN: None | ca certs loaded: 342
Reproduce / 如何复现
在 AstrBot 自带 Python 环境下运行(需要一个 OpenAI 兼容中转站的 key,我这里用 78code):
import asyncio, httpx
from astrbot.core.utils import network_utils as nu
from openai import AsyncOpenAI
KEY = "sk-***"
BASE = "https://www.78code.cc/v1"
async def try_client(label, client):
c = AsyncOpenAI(api_key=KEY, base_url=BASE, timeout=60, max_retries=0, http_client=client)
try:
r = await c.chat.completions.create(
model="gemini-3-flash-preview",
messages=[{"role": "user", "content": "say OK"}],
max_tokens=16,
)
print(label, "-> OK:", r.choices[0].message.content)
except Exception as e:
print(label, "-> EXC:", type(e).__name__, e)
finally:
await c.close()
async def main():
await try_client("hybrid-ctx", httpx.AsyncClient(verify=nu._SYSTEM_SSL_CTX))
await try_client("default", httpx.AsyncClient())
asyncio.run(main())
实际输出:
hybrid-ctx -> EXC: APITimeoutError Request timed out.
cause: ReadTimeout
default -> OK: 'OK'
期望行为 / Expected
- 不应因为「用了系统证书库」而让某些站点完全不可用。
- 建议二选一或都做:
- 给 provider 配置增加一个开关(例如
skip_system_ssl_store/ssl_verify: default|certifi|system),让用户能对这类中转站退回 certifi 默认校验; - 或者在
create_proxy_client里做兜底:当使用混合上下文出现ReadTimeout/ConnectError时,用默认校验重试一次,并在日志里标注降级原因。
- 给 provider 配置增加一个开关(例如
- 顺带一提:
APIConnectionError: Connection error.这个报错信息完全无法区分「DNS 失败 / TLS 挂住 / 上游拒绝」,排查成本很高(参见 #9527)。
AstrBot version, deployment method, provider, messaging platform / 版本与部署方式
- AstrBot:v4.29.4(Desktop 版,Windows)
- Python:3.12.12(AstrBot 内置运行时)
- httpx:0.28.1;openai SDK:3.42.0
- OS:Windows 11 家庭中文版,build 26200
- Provider:OpenAI 兼容(
https://www.78code.cc/v1),模型gemini-3-flash-preview/gemini-4-argon/gemini-3.8-flash - 消息平台:aiocqhttp(NapCat 反向 WebSocket)
- 同一实例下 DeepSeek / SiliconFlow 等 provider 均正常,只有这个中转站受影响
OS
Windows 11 (26200)
Logs / 报错日志
见上方「What happened」段落中的 5 次重试日志;完整失败链路为:
openai.APIConnectionError: Connection error.
← openai.APITimeoutError / ReadTimeout
← httpx.AsyncClient(verify=<system+certifi 混合上下文>)
Dashboard /api/v1/providers/test 返回:
{"id":"78code/gemini-3-flash-preview","model":"gemini-3-flash-preview",
"type":"chat_completion","status":"unavailable","error":"Connection error."}
Are you willing to submit a PR? / 你愿意提交 PR 吗?
- Yes!(如需,我可以提供补丁:给 provider 增加证书校验策略开关,并在 ReadTimeout 时降级重试)
Code of Conduct
- I have read and agree to abide by the project's Code of Conduct.
- Lenguaje dominante
- Python
- Estrellas
- 46.4k
- Forks
- 3.4k
- Merge medio
- 1 d 11 h
- PR fusionados (30 d)
- 120
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de AstrBotDevs/AstrBot
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
AstrBotDevs/AstrBot#11038 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
AstrBotDevs/AstrBot#11034 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
AstrBotDevs/AstrBot#10999 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
[Bug] OpenAI-compatible Gemini 工具 schema const 导致 400Posiblemente ocupada @arccat-114 la tomó hace 17 días. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
AstrBotDevs/AstrBot#10720 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
[Bug] Discord slash command sync aborts with KeyError when a plugin handler is defined in a submodulePosiblemente ocupada @yuexia5 la tomó hace 22 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
AstrBotDevs/AstrBot#10626 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de AstrBotDevs/AstrBot
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
bytedance/agentkit-samples#277 ·
-
ai-driven-fuzzing bug claude Minor
Dificultad 2/5 1-3 horas Aptitud para principiantes 83/100
Los mantenedores suelen responder en 2 días
-
Edit: TVS Encarnacion [PY]Abiertochannels:edit check:passed
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
[Bug] The blue-green mitigation strategy lists catalog names and views before the source tablesAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
aws-samples/sample-aws-genai-db-modernizer#2097 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día