Bug Description
On iOS, custom OpenAI-compatible endpoints with API key authentication fail to work. The same configuration (custom base URL + API key) works correctly on desktop. Disabling authentication on the server side makes it work on iOS.
Steps to Reproduce
- Open Chatbox on iOS
- Configure a custom OpenAI-compatible provider with a custom endpoint URL and API key (e.g., LM Studio with auth enabled)
- Send a message
- Request fails
Expected Results
The request should succeed and return a chat completion, the same as on desktop.
Actual Results
The request fails on iOS. The server (e.g., LM Studio) logs: "Unexpected endpoint or method. (POST /v1/chat/completions). Returning 200 anyway". Disabling auth on the server makes it work on iOS.
Screenshots
N/A
Desktop (please complete the following information):
- Operating System: iOS 26.3
- Application Version: Chatbox v1.19.0
Additional Context
Root Cause Analysis (suggested by Claude, not verified manually)
In src/renderer/utils/request.ts, doRequest only routes mobile requests through handleMobileRequest (which uses CapacitorHttp / native HTTP) when useProxy is true:
// request.ts line 64-66
if (platform.type === 'mobile' && useProxy) { // BUG: requires useProxy to be true
return handleMobileRequest(requestUrl, method, headers, body, signal)
}
// Falls through to plain fetch() in WKWebView
const res = await fetch(requestUrl, { method, headers, body, signal })
Why this fails on iOS but works on desktop:
- Desktop (Electron):
webSecurity: false is set in src/main/main.ts:266, disabling CORS enforcement entirely. Plain fetch() works without restrictions.
- iOS (Capacitor/WKWebView): CORS is enforced. The
Authorization: Bearer <key> header is a non-simple header that triggers a CORS preflight (OPTIONS request). Servers like LM Studio don't handle the OPTIONS preflight properly, causing the request to fail or lose its auth header.
Why disabling auth fixes it: Without the Authorization header, no CORS preflight is triggered and the request goes through as a "simple request".
Why CapacitorHttp fixes it: CapacitorHttp makes native HTTP calls via iOS URLSession, bypassing WKWebView's CORS enforcement entirely — no preflight, no header restrictions.
For custom OpenAI-compatible endpoints, useProxy is typically false/undefined, so on iOS the request falls through to plain fetch() instead of CapacitorHttp.
Suggested Fix (suggested by Claude, not verified manually)
On mobile, always route through handleMobileRequest regardless of useProxy:
// Change from:
if (platform.type === 'mobile' && useProxy) {
// To:
if (platform.type === 'mobile') {
Bug Description
On iOS, custom OpenAI-compatible endpoints with API key authentication fail to work. The same configuration (custom base URL + API key) works correctly on desktop. Disabling authentication on the server side makes it work on iOS.
Steps to Reproduce
Expected Results
The request should succeed and return a chat completion, the same as on desktop.
Actual Results
The request fails on iOS. The server (e.g., LM Studio) logs: "Unexpected endpoint or method. (POST /v1/chat/completions). Returning 200 anyway". Disabling auth on the server makes it work on iOS.
Screenshots
N/A
Desktop (please complete the following information):
Additional Context
Root Cause Analysis (suggested by Claude, not verified manually)
In
src/renderer/utils/request.ts,doRequestonly routes mobile requests throughhandleMobileRequest(which usesCapacitorHttp/ native HTTP) whenuseProxyistrue:Why this fails on iOS but works on desktop:
webSecurity: falseis set insrc/main/main.ts:266, disabling CORS enforcement entirely. Plainfetch()works without restrictions.Authorization: Bearer <key>header is a non-simple header that triggers a CORS preflight (OPTIONS request). Servers like LM Studio don't handle the OPTIONS preflight properly, causing the request to fail or lose its auth header.Why disabling auth fixes it: Without the
Authorizationheader, no CORS preflight is triggered and the request goes through as a "simple request".Why
CapacitorHttpfixes it:CapacitorHttpmakes native HTTP calls via iOS URLSession, bypassing WKWebView's CORS enforcement entirely — no preflight, no header restrictions.For custom OpenAI-compatible endpoints,
useProxyis typicallyfalse/undefined, so on iOS the request falls through to plainfetch()instead ofCapacitorHttp.Suggested Fix (suggested by Claude, not verified manually)
On mobile, always route through
handleMobileRequestregardless ofuseProxy: