fix: replace misleading webui CORS error toast with generic network error message

Browsers throw an opaque TypeError for all network-level failures — CORS
policy violations, TLS/certificate rejections (e.g. self-signed certs), DNS
failures, and unreachable hosts — with no way to distinguish between them.
Asserting "CORS blocked" on every TypeError caused users to chase a CORS
misconfiguration when the real problem was an untrusted certificate or
unreachable gateway. The error message now lists some plausible causes so users
have possible diagnostics regardless of the actual failure mode.

Fixes #2143
This commit is contained in:
Ben McClelland
2026-05-29 21:10:22 -07:00
parent f7cc70b157
commit d498d48497
2 changed files with 10 additions and 7 deletions
+1 -1
View File
@@ -633,7 +633,7 @@ under the License.
api.logout();
console.error('Login error:', error);
if (error.message.includes('CORS blocked')) {
if (error.message.startsWith('Network error:')) {
showError(error.message);
} else if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) {
showError('Unable to connect to the gateway. Please check the endpoint URL and ensure the server is running.');
+9 -6
View File
@@ -717,9 +717,10 @@ class VersityAPI {
signal: signal || undefined,
});
} catch (e) {
// Browsers surface CORS blocks as a generic TypeError.
// Browsers surface network-level failures (CORS, TLS/certificate errors,
// unreachable host) as a generic TypeError with no further detail.
if (e instanceof TypeError) {
throw new Error(`CORS blocked by gateway. Allow origin ${window.location.origin} and headers Authorization, X-Amz-Date, X-Amz-Content-Sha256, Content-Type.`);
throw new Error(`Network error: cannot reach gateway. Common causes: CORS policy (gateway must allow origin ${window.location.origin}), TLS/certificate error (untrusted or self-signed certificate rejected by the browser), or the gateway is unreachable.`);
}
throw e;
}
@@ -788,9 +789,9 @@ class VersityAPI {
await this.listBucketsS3();
this.setAdminRole(false);
} catch (s3Error) {
// If the gateway is reachable but the browser blocks the response due to CORS,
// surface that as an error so the UI can show a useful message.
if (s3Error && typeof s3Error.message === 'string' && s3Error.message.includes('CORS blocked')) {
// If the request failed at the network level (CORS, TLS, or unreachable),
// surface that error so the UI can show a useful diagnostic message.
if (s3Error && typeof s3Error.message === 'string' && s3Error.message.startsWith('Network error:')) {
throw s3Error;
}
return 'none';
@@ -931,8 +932,10 @@ class VersityAPI {
try {
httpResponse = await fetch(presignedUrl, { method: 'GET' });
} catch (e) {
// Browsers surface network-level failures (CORS, TLS/certificate errors,
// unreachable host) as a generic TypeError with no further detail.
if (e instanceof TypeError) {
throw new Error(`CORS blocked by gateway. Allow origin ${window.location.origin} for S3 responses (GET / and bucket/object operations).`);
throw new Error(`Network error: cannot reach gateway. Common causes: CORS policy (gateway must allow origin ${window.location.origin}), TLS/certificate error (untrusted or self-signed certificate rejected by the browser), or the gateway is unreachable.`);
}
throw e;
}