HKUDS/DeepTutor · error · LLMAPIError
Connection to {binding} API was forcibly closed. This may in
Error message
Connection to {binding} API was forcibly closed. This may indicate network issues or server-side problems. Please check your internet connection and try again. What it means
Caught as an aiohttp.ClientError whose string contains 'forcibly closed' or Winsock error 10054 — the TCP connection was reset mid-request. _openai_complete maps it to LLMAPIError with status_code=0 and a user-friendly remediation message, chaining the original exception. It is a transport-level failure, not an API-level one.
Source
Thrown at deeptutor/services/llm/cloud_provider.py:434
cast(dict[str, object], message)
)
else:
retry_text = await retry_resp.text()
raise LLMAPIError(
f"OpenAI API error: {retry_text}",
status_code=retry_resp.status,
provider=binding or "openai",
)
else:
raise LLMAPIError(
f"OpenAI API error: {error_text}",
status_code=resp.status,
provider=binding or "openai",
)
except aiohttp.ClientError as e:
# Handle connection errors with more specific messages
if "forcibly closed" in str(e).lower() or "10054" in str(e):
raise LLMAPIError(
f"Connection to {binding} API was forcibly closed. "
"This may indicate network issues or server-side problems. "
"Please check your internet connection and try again.",
status_code=0,
provider=binding or "openai",
) from e
else:
raise LLMAPIError(
f"Network error connecting to {binding} API: {e}",
status_code=0,
provider=binding or "openai",
) from e
if content is not None:
# Clean thinking tags from response using unified utility
return clean_thinking_tags(content, binding, model)
raise LLMConfigError("Cloud completion failed: no valid configuration")View on GitHub (pinned to 3e82f13042)
Solutions
- Retry the request with exponential backoff (status_code=0 marks it transient).
- Check network path: disable VPN/proxy temporarily, test curl to the same base_url.
- Reduce request duration (smaller max_tokens, enable streaming) so the connection is short-lived.
- If persistent, point base_url at a reachable mirror or deploy closer egress.
Example fix
// before
out = await complete(prompt=p, model=m)
# after
for attempt in range(3):
try:
out = await complete(prompt=p, model=m)
break
except LLMAPIError as e:
if e.status_code != 0 or attempt == 2:
raise
await asyncio.sleep(2 ** attempt) Defensive patterns
Strategy: retry
Validate before calling
# Cannot validate a remote reset in advance; keep requests short instead:
kwargs["max_tokens"] = min(int(kwargs.get("max_tokens", 1024)), 1024) Try / catch
for attempt in range(3):
try:
out = await complete(prompt=p, model=m)
break
except LLMAPIError as e:
if e.status_code != 0 or attempt == 2:
raise
await asyncio.sleep(2 ** attempt) Prevention
- Treat status_code==0 as transient and retry with backoff.
- Prefer streaming for long generations to keep each connection short.
- Monitor network health (VPN/proxy) when long completions fail repeatedly.
When it happens
Trigger: Remote server or an intermediate firewall/NAT/proxy drops the TCP connection during a long completion; flaky Wi-Fi or VPN dropout; Windows environments where 10054 surfaces in the exception text; cloud LLM endpoints closing idle long-running requests.
Common situations: Corporate proxies killing long-lived POSTs; mobile/tethered networks; long streaming completions on unstable links; regional network blocking of the provider endpoint.
Related errors
- Network error connecting to {binding} API: {e}
- Codex transport request failed.
- Channel onboarding provider request failed ({type(exc).__nam
- This model is not assigned to your account.
- The model returned an empty response.
AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27).
Data as JSON: /api/errors/d8143fe584cfd88f.
Report an issue: GitHub.