From ca714b8cf171f393e2f8cc823e3e89b7842b5cf1 Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Fri, 18 Sep 2026 15:14:58 +0200
Subject: [PATCH 1/2] fix: accept the full cluster URL in the Weaviate Cloud
connection helpers
Connect.Cloud, WeaviateClientBuilder.Cloud and the three AddWeaviateCloud
overloads used the endpoint verbatim as the host, so the URL the Weaviate
Cloud console shows (https://my-cluster.weaviate.cloud) could not connect.
They now accept the URL or a bare hostname, keep only the host, and reject
anything else with an ArgumentException; the DI overloads reject it at
registration. An http:// scheme is ignored: Cloud always uses TLS on 443.
---
.../Unit/TestCloudEndpoint.cs | 311 ++++++++++++++++++
src/Weaviate.Client/ConnectionHelpers.cs | 13 +
.../WeaviateServiceCollectionExtensions.cs | 39 ++-
src/Weaviate.Client/Internal/CloudEndpoint.cs | 81 +++++
src/Weaviate.Client/WeaviateClientBuilder.cs | 19 +-
5 files changed, 449 insertions(+), 14 deletions(-)
create mode 100644 src/Weaviate.Client.Tests/Unit/TestCloudEndpoint.cs
create mode 100644 src/Weaviate.Client/Internal/CloudEndpoint.cs
diff --git a/src/Weaviate.Client.Tests/Unit/TestCloudEndpoint.cs b/src/Weaviate.Client.Tests/Unit/TestCloudEndpoint.cs
new file mode 100644
index 00000000..dae4514a
--- /dev/null
+++ b/src/Weaviate.Client.Tests/Unit/TestCloudEndpoint.cs
@@ -0,0 +1,311 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Weaviate.Client.DependencyInjection;
+
+namespace Weaviate.Client.Tests.Unit;
+
+///
+/// Unit tests verifying that the Weaviate Cloud connection helpers accept a full cluster URL or a
+/// bare hostname, derive the REST and gRPC endpoints from its host, and reject malformed endpoints.
+///
+[Collection("Unit Tests")]
+public class TestCloudEndpoint
+{
+ ///
+ /// Accepted cluster endpoints and the host each one must resolve to
+ ///
+ public static TheoryData AcceptedEndpoints =>
+ new()
+ {
+ { "https://xyz.weaviate.cloud", "xyz.weaviate.cloud" },
+ { "xyz.weaviate.cloud", "xyz.weaviate.cloud" },
+ { "https://xyz.weaviate.cloud/", "xyz.weaviate.cloud" },
+ { "https://xyz.weaviate.cloud/v1", "xyz.weaviate.cloud" },
+ { "http://xyz.weaviate.cloud", "xyz.weaviate.cloud" },
+ { "https://xyz.weaviate.cloud:443", "xyz.weaviate.cloud" },
+ { " https://xyz.weaviate.cloud/v1?x=1#top ", "xyz.weaviate.cloud" },
+ { "HTTPS://XYZ.Weaviate.Cloud", "xyz.weaviate.cloud" },
+ { "xyz.weaviate.cloud:443", "xyz.weaviate.cloud" },
+ { "xyz.weaviate.cloud/a://b", "xyz.weaviate.cloud" },
+ { "https://https.weaviate.cloud", "https.weaviate.cloud" },
+ };
+
+ ///
+ /// Cluster endpoints that must be rejected, each with a fragment of it that the exception
+ /// message must not echo (null where the input has nothing distinctive)
+ ///
+ public static TheoryData RejectedEndpoints =>
+ new()
+ {
+ { null, null },
+ { "", null },
+ { " ", null },
+ { "https://", null },
+ { "ftp://xyz.weaviate.cloud", "ftp" },
+ { "sk-SECRET123://xyz.weaviate.cloud", "SECRET123" },
+ { "https://user:pw@xyz.weaviate.cloud", "pw" },
+ { "https://xyz.weaviate.cloud:8080", "8080" },
+ { "xyz.weaviate.cloud:8080", "8080" },
+ { "http://xyz.weaviate.cloud:80", ":80" },
+ { "https://10.0.0.1", "10.0.0.1" },
+ { "https://https://xyz.weaviate.cloud", "xyz" },
+ { "https:/xyz.weaviate.cloud", "xyz" },
+ { "https//xyz.weaviate.cloud", "xyz" },
+ { "not a host!", "not a host" },
+ };
+
+ ///
+ /// Tests that AddWeaviateCloud keeps only the host and always uses TLS on port 443
+ ///
+ [Theory]
+ [MemberData(nameof(AcceptedEndpoints))]
+ public void AddWeaviateCloud_WithUrlOrHostname_UsesClusterHost(
+ string endpoint,
+ string expectedHost
+ )
+ {
+ using var provider = new ServiceCollection()
+ .AddWeaviateCloud(endpoint, apiKey: "key", eagerInitialization: false)
+ .BuildServiceProvider();
+
+ AssertCloudOptions(
+ provider.GetRequiredService>().Value,
+ expectedHost
+ );
+ AssertCloudUris(provider.GetRequiredService(), expectedHost);
+ }
+
+ ///
+ /// Tests that AddWeaviateCloud rejects a malformed endpoint when it is registered, not when
+ /// the client is first resolved
+ ///
+ [Theory]
+ [MemberData(nameof(RejectedEndpoints))]
+ public void AddWeaviateCloud_WithInvalidEndpoint_ThrowsAtRegistration(
+ string? endpoint,
+ string? mustNotEcho
+ )
+ {
+ var services = new ServiceCollection();
+
+ var ex = Assert.Throws(() =>
+ services.AddWeaviateCloud(endpoint!, eagerInitialization: false)
+ );
+
+ AssertRejected(ex, "clusterEndpoint", mustNotEcho);
+ }
+
+ ///
+ /// Tests that the scoped token service overload of AddWeaviateCloud keeps only the host
+ ///
+ [Fact]
+ public void AddWeaviateCloudWithTokenService_WithUrl_UsesClusterHost()
+ {
+ using var provider = new ServiceCollection()
+ .AddWeaviateCloud("https://xyz.weaviate.cloud/")
+ .BuildServiceProvider();
+
+ AssertCloudOptions(
+ provider.GetRequiredService>().Value,
+ "xyz.weaviate.cloud"
+ );
+ AssertCloudUris(provider.GetRequiredService(), "xyz.weaviate.cloud");
+ }
+
+ ///
+ /// Tests that the scoped token service overload of AddWeaviateCloud rejects a malformed endpoint
+ /// at registration
+ ///
+ [Fact]
+ public void AddWeaviateCloudWithTokenService_WithInvalidEndpoint_ThrowsAtRegistration()
+ {
+ var services = new ServiceCollection();
+
+ var ex = Assert.Throws(() =>
+ services.AddWeaviateCloud("https://xyz.weaviate.cloud:8080")
+ );
+
+ AssertRejected(ex, "clusterEndpoint", "8080");
+ }
+
+ ///
+ /// Tests that the named client overload of AddWeaviateCloud keeps only the host
+ ///
+ [Fact]
+ public void AddWeaviateCloudNamed_WithUrl_UsesClusterHost()
+ {
+ using var provider = new ServiceCollection()
+ .AddWeaviateCloud(
+ name: "prod",
+ clusterEndpoint: "https://xyz.weaviate.cloud/",
+ apiKey: "key"
+ )
+ .BuildServiceProvider();
+
+ AssertCloudOptions(
+ provider.GetRequiredService>().Get("prod"),
+ "xyz.weaviate.cloud"
+ );
+ }
+
+ ///
+ /// Tests that the named client overload of AddWeaviateCloud rejects a malformed endpoint at
+ /// registration
+ ///
+ [Fact]
+ public void AddWeaviateCloudNamed_WithInvalidEndpoint_ThrowsAtRegistration()
+ {
+ var services = new ServiceCollection();
+
+ var ex = Assert.Throws(() =>
+ services.AddWeaviateCloud(name: "prod", clusterEndpoint: "ftp://xyz.weaviate.cloud")
+ );
+
+ AssertRejected(ex, "clusterEndpoint", "ftp");
+ }
+
+ ///
+ /// Tests that a client built with WeaviateClientBuilder.Cloud sends its first REST request to
+ /// the cluster host over TLS on port 443
+ ///
+ [Theory]
+ [MemberData(nameof(AcceptedEndpoints))]
+ public async Task Cloud_WithUrlOrHostname_SendsRequestsToClusterHost(
+ string endpoint,
+ string expectedHost
+ )
+ {
+ var handler = new CaptureFirstRequestHandler();
+
+ await Assert.ThrowsAsync(() =>
+ WeaviateClientBuilder.Cloud(endpoint, httpMessageHandler: handler).BuildAsync()
+ );
+
+ AssertCloudRequest(handler.RequestUri, expectedHost);
+ }
+
+ ///
+ /// Tests that WeaviateClientBuilder.Cloud rejects a malformed endpoint before any request is made
+ ///
+ [Theory]
+ [MemberData(nameof(RejectedEndpoints))]
+ public void Cloud_WithInvalidEndpoint_Throws(string? endpoint, string? mustNotEcho)
+ {
+ var ex = Assert.Throws(() =>
+ {
+ WeaviateClientBuilder.Cloud(endpoint!);
+ });
+
+ AssertRejected(ex, "restEndpoint", mustNotEcho);
+ }
+
+ ///
+ /// Tests that Connect.Cloud sends its first REST request to the cluster host
+ ///
+ [Fact]
+ public async Task ConnectCloud_WithUrl_SendsRequestsToClusterHost()
+ {
+ var handler = new CaptureFirstRequestHandler();
+
+ await Assert.ThrowsAsync(() =>
+ Connect.Cloud("https://xyz.weaviate.cloud/", httpMessageHandler: handler)
+ );
+
+ AssertCloudRequest(handler.RequestUri, "xyz.weaviate.cloud");
+ }
+
+ ///
+ /// Tests that Connect.Cloud rejects a malformed endpoint
+ ///
+ [Fact]
+ public async Task ConnectCloud_WithInvalidEndpoint_Throws()
+ {
+ var ex = await Assert.ThrowsAsync(() =>
+ Connect.Cloud("https://user:pw@xyz.weaviate.cloud")
+ );
+
+ AssertRejected(ex, "restEndpoint", "pw");
+ }
+
+ ///
+ /// Asserts that a rejection names the parameter and the expected forms, and does not echo the input
+ ///
+ private static void AssertRejected(ArgumentException ex, string paramName, string? mustNotEcho)
+ {
+ Assert.Equal(paramName, ex.ParamName);
+ Assert.Contains("'https://my-cluster.weaviate.cloud'", ex.Message);
+ if (mustNotEcho is not null)
+ {
+ Assert.DoesNotContain(mustNotEcho, ex.Message);
+ }
+ }
+
+ ///
+ /// Asserts the options registered for a Weaviate Cloud client
+ ///
+ private static void AssertCloudOptions(WeaviateOptions options, string expectedHost)
+ {
+ Assert.Equal(expectedHost, options.RestEndpoint);
+ Assert.Equal($"grpc-{expectedHost}", options.GrpcEndpoint);
+ Assert.Equal((ushort)443, options.RestPort);
+ Assert.Equal((ushort)443, options.GrpcPort);
+ Assert.True(options.UseSsl);
+ }
+
+ ///
+ /// Asserts the REST and gRPC URIs of a client resolved for Weaviate Cloud
+ ///
+ private static void AssertCloudUris(WeaviateClient client, string expectedHost)
+ {
+ Assert.Equal($"https://{expectedHost}/v1/", client.Configuration.RestUri.AbsoluteUri);
+ Assert.Equal($"https://grpc-{expectedHost}/", client.Configuration.GrpcUri.AbsoluteUri);
+ }
+
+ ///
+ /// Asserts the URI of the first REST request a Weaviate Cloud client sends
+ ///
+ private static void AssertCloudRequest(Uri? requestUri, string expectedHost)
+ {
+ Assert.NotNull(requestUri);
+ Assert.Equal("https", requestUri.Scheme);
+ Assert.Equal(expectedHost, requestUri.Host);
+ Assert.Equal(443, requestUri.Port);
+ Assert.Equal("/v1/meta", requestUri.AbsolutePath);
+ }
+
+ ///
+ /// Records the first request and stops client initialization before any network call
+ ///
+ private sealed class CaptureFirstRequestHandler : HttpMessageHandler
+ {
+ public Uri? RequestUri { get; private set; }
+
+ protected override Task SendAsync(
+ HttpRequestMessage request,
+ CancellationToken cancellationToken
+ )
+ {
+ RequestUri ??= request.RequestUri;
+ throw new RequestCapturedException();
+ }
+ }
+
+ ///
+ /// Thrown by once the request is recorded
+ ///
+ private sealed class RequestCapturedException : Exception;
+
+ ///
+ /// A token service that is never called by these tests
+ ///
+ private sealed class StubTokenService : ITokenService
+ {
+ public Task GetAccessTokenAsync(CancellationToken cancellationToken = default) =>
+ Task.FromResult("token");
+
+ public Task RefreshTokenAsync(CancellationToken cancellationToken = default) =>
+ Task.FromResult(true);
+
+ public bool IsAuthenticated() => true;
+ }
+}
diff --git a/src/Weaviate.Client/ConnectionHelpers.cs b/src/Weaviate.Client/ConnectionHelpers.cs
index fd939804..366c3685 100644
--- a/src/Weaviate.Client/ConnectionHelpers.cs
+++ b/src/Weaviate.Client/ConnectionHelpers.cs
@@ -51,6 +51,19 @@ public static Task Local(
///
/// Creates a WeaviateClient connecting to Weaviate Cloud.
///
+ /// The cluster URL (e.g. https://my-cluster.weaviate.cloud) or its
+ /// bare hostname (e.g. my-cluster.weaviate.cloud). Only the host is used: the scheme, including
+ /// http://, and any path are ignored, because Weaviate Cloud always uses TLS on port 443.
+ /// API key for authentication.
+ /// Additional HTTP headers to include in requests.
+ /// Optional HTTP message handler for REST requests.
+ /// Default timeout for all operations.
+ /// Timeout for initialization operations.
+ /// Timeout for data operations.
+ /// Timeout for query operations.
+ /// is empty, uses a scheme other
+ /// than http or https, contains user credentials, specifies a port other than 443, or does not have a
+ /// valid DNS hostname.
public static Task Cloud(
string restEndpoint,
string? apiKey = null,
diff --git a/src/Weaviate.Client/DependencyInjection/WeaviateServiceCollectionExtensions.cs b/src/Weaviate.Client/DependencyInjection/WeaviateServiceCollectionExtensions.cs
index 8ff9adc5..d58dc7ca 100644
--- a/src/Weaviate.Client/DependencyInjection/WeaviateServiceCollectionExtensions.cs
+++ b/src/Weaviate.Client/DependencyInjection/WeaviateServiceCollectionExtensions.cs
@@ -154,7 +154,9 @@ public static IServiceCollection AddWeaviateLocal(
/// Similar to Connect.Cloud() but for dependency injection.
///
/// The service collection.
- /// The Weaviate Cloud cluster endpoint (e.g., "my-cluster.weaviate.cloud").
+ /// The cluster URL (e.g. "https://my-cluster.weaviate.cloud") or its bare
+ /// hostname (e.g. "my-cluster.weaviate.cloud"). Only the host is used: the scheme, including http://,
+ /// and any path are ignored, because Weaviate Cloud always uses TLS on port 443.
/// API key for authentication.
/// Additional HTTP headers to include in requests.
/// Default timeout for all operations.
@@ -163,6 +165,9 @@ public static IServiceCollection AddWeaviateLocal(
/// Timeout for query operations.
/// Whether to initialize the client eagerly on application startup. Default is true.
/// The service collection for method chaining.
+ /// is empty, uses a scheme other
+ /// than http or https, contains user credentials, specifies a port other than 443, or does not have a
+ /// valid DNS hostname.
public static IServiceCollection AddWeaviateCloud(
this IServiceCollection services,
string clusterEndpoint,
@@ -175,11 +180,13 @@ public static IServiceCollection AddWeaviateCloud(
bool eagerInitialization = true
)
{
+ var host = Internal.CloudEndpoint.NormalizeHost(clusterEndpoint, nameof(clusterEndpoint));
+
services.AddWeaviate(
options =>
{
- options.RestEndpoint = clusterEndpoint;
- options.GrpcEndpoint = $"grpc-{clusterEndpoint}";
+ options.RestEndpoint = host;
+ options.GrpcEndpoint = $"grpc-{host}";
options.RestPort = 443;
options.GrpcPort = 443;
options.UseSsl = true;
@@ -250,10 +257,15 @@ public static IServiceCollection AddWeaviate(
///
/// A scoped implementation.
/// The service collection.
- /// The Weaviate Cloud cluster endpoint (e.g. "my-cluster.weaviate.cloud").
+ /// The cluster URL (e.g. "https://my-cluster.weaviate.cloud") or its bare
+ /// hostname (e.g. "my-cluster.weaviate.cloud"). Only the host is used: the scheme, including http://,
+ /// and any path are ignored, because Weaviate Cloud always uses TLS on port 443.
/// Whether to initialize eagerly on startup. Defaults to false
/// because the token service may depend on request context unavailable at startup.
/// The service collection for method chaining.
+ /// is empty, uses a scheme other
+ /// than http or https, contains user credentials, specifies a port other than 443, or does not have a
+ /// valid DNS hostname.
public static IServiceCollection AddWeaviateCloud(
this IServiceCollection services,
string clusterEndpoint,
@@ -261,11 +273,13 @@ public static IServiceCollection AddWeaviateCloud(
)
where TTokenService : class, ITokenService
{
+ var host = Internal.CloudEndpoint.NormalizeHost(clusterEndpoint, nameof(clusterEndpoint));
+
return services.AddWeaviate(
options =>
{
- options.RestEndpoint = clusterEndpoint;
- options.GrpcEndpoint = $"grpc-{clusterEndpoint}";
+ options.RestEndpoint = host;
+ options.GrpcEndpoint = $"grpc-{host}";
options.RestPort = 443;
options.GrpcPort = 443;
options.UseSsl = true;
@@ -385,7 +399,9 @@ Action configureOptions
///
/// The service collection.
/// The logical name of the client.
- /// The Weaviate Cloud cluster endpoint (e.g., "my-cluster.weaviate.cloud").
+ /// The cluster URL (e.g. "https://my-cluster.weaviate.cloud") or its bare
+ /// hostname (e.g. "my-cluster.weaviate.cloud"). Only the host is used: the scheme, including http://,
+ /// and any path are ignored, because Weaviate Cloud always uses TLS on port 443.
/// API key for authentication.
/// Additional HTTP headers to include in requests.
/// Default timeout for all operations.
@@ -393,6 +409,9 @@ Action configureOptions
/// Timeout for data operations.
/// Timeout for query operations.
/// The service collection for method chaining.
+ /// is empty, uses a scheme other
+ /// than http or https, contains user credentials, specifies a port other than 443, or does not have a
+ /// valid DNS hostname.
public static IServiceCollection AddWeaviateCloud(
this IServiceCollection services,
string name,
@@ -405,12 +424,14 @@ public static IServiceCollection AddWeaviateCloud(
TimeSpan? queryTimeout = null
)
{
+ var host = Internal.CloudEndpoint.NormalizeHost(clusterEndpoint, nameof(clusterEndpoint));
+
return services.AddWeaviateClient(
name,
options =>
{
- options.RestEndpoint = clusterEndpoint;
- options.GrpcEndpoint = $"grpc-{clusterEndpoint}";
+ options.RestEndpoint = host;
+ options.GrpcEndpoint = $"grpc-{host}";
options.RestPort = 443;
options.GrpcPort = 443;
options.UseSsl = true;
diff --git a/src/Weaviate.Client/Internal/CloudEndpoint.cs b/src/Weaviate.Client/Internal/CloudEndpoint.cs
new file mode 100644
index 00000000..26982c37
--- /dev/null
+++ b/src/Weaviate.Client/Internal/CloudEndpoint.cs
@@ -0,0 +1,81 @@
+namespace Weaviate.Client.Internal;
+
+///
+/// Normalizes the cluster endpoint passed to the Weaviate Cloud connection helpers.
+///
+internal static class CloudEndpoint
+{
+ private const string ExpectedForms =
+ "Expected a Weaviate Cloud cluster URL such as 'https://my-cluster.weaviate.cloud' or a bare hostname such as 'my-cluster.weaviate.cloud'.";
+
+ ///
+ /// Returns the host of a Weaviate Cloud cluster URL or bare hostname. The scheme, path, query
+ /// and fragment are dropped, because Weaviate Cloud is always reached over TLS on port 443.
+ ///
+ /// A cluster URL such as https://my-cluster.weaviate.cloud, or a bare hostname.
+ /// The caller's parameter name, reported in the exception.
+ /// The bare, lowercased host, e.g. my-cluster.weaviate.cloud.
+ ///
+ /// The endpoint is empty, uses a scheme other than http or https, contains user credentials,
+ /// specifies a port other than 443, or does not have a valid DNS hostname.
+ ///
+ internal static string NormalizeHost(string? endpoint, string paramName)
+ {
+ var value = endpoint?.Trim();
+ if (string.IsNullOrEmpty(value))
+ throw Invalid("The Weaviate Cloud cluster endpoint is empty.", paramName);
+
+ var hostAndRest = value;
+ var schemeEnd = value.IndexOf("://", StringComparison.Ordinal);
+ if (schemeEnd > 0 && Uri.CheckSchemeName(value[..schemeEnd]))
+ {
+ var scheme = value[..schemeEnd];
+ if (
+ !scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)
+ && !scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)
+ )
+ {
+ throw Invalid("Only the http and https schemes are supported.", paramName);
+ }
+ hostAndRest = value[(schemeEnd + 3)..];
+ }
+
+ // Parsed as https whatever the given scheme, so an absent port means 443, never 80.
+ if (!Uri.TryCreate("https://" + hostAndRest, UriKind.Absolute, out var uri))
+ throw Invalid(
+ "The Weaviate Cloud cluster endpoint is not a valid URL or hostname.",
+ paramName
+ );
+
+ if (uri.UserInfo.Length > 0)
+ throw Invalid(
+ "The Weaviate Cloud cluster endpoint must not contain user credentials; pass the API key separately.",
+ paramName
+ );
+
+ if (uri.Port != 443)
+ throw Invalid(
+ "Weaviate Cloud is served on port 443; other ports are not supported.",
+ paramName
+ );
+
+ if (Uri.CheckHostName(uri.Host) != UriHostNameType.Dns)
+ throw Invalid(
+ "The Weaviate Cloud cluster endpoint does not have a valid DNS hostname.",
+ paramName
+ );
+
+ // A mistyped scheme ("https:/x", "https://https://x") parses as the host "https".
+ if (uri.Host is "http" or "https")
+ throw Invalid(
+ "The Weaviate Cloud cluster endpoint is not a valid URL or hostname.",
+ paramName
+ );
+
+ return uri.Host;
+ }
+
+ // Never echo the input: it may be a misplaced API key or carry a password.
+ private static ArgumentException Invalid(string reason, string paramName) =>
+ new($"{reason} {ExpectedForms}", paramName);
+}
diff --git a/src/Weaviate.Client/WeaviateClientBuilder.cs b/src/Weaviate.Client/WeaviateClientBuilder.cs
index 766d7c48..b546b5da 100644
--- a/src/Weaviate.Client/WeaviateClientBuilder.cs
+++ b/src/Weaviate.Client/WeaviateClientBuilder.cs
@@ -178,26 +178,35 @@ public static WeaviateClientBuilder Local(
///
/// Clouds the rest endpoint
///
- /// The rest endpoint
+ /// The cluster URL (e.g. https://my-cluster.weaviate.cloud) or its
+ /// bare hostname (e.g. my-cluster.weaviate.cloud). Only the host is used: the scheme, including
+ /// http://, and any path are ignored, because Weaviate Cloud always uses TLS on port 443.
/// The api key
/// The headers
/// The http message handler
/// The weaviate client builder
+ /// is empty, uses a scheme other
+ /// than http or https, contains user credentials, specifies a port other than 443, or does not have a
+ /// valid DNS hostname.
public static WeaviateClientBuilder Cloud(
string restEndpoint,
string? apiKey = null,
Dictionary? headers = null,
HttpMessageHandler? httpMessageHandler = null
- ) =>
- new WeaviateClientBuilder()
- .WithRestEndpoint(restEndpoint)
- .WithGrpcEndpoint($"grpc-{restEndpoint}")
+ )
+ {
+ var host = Internal.CloudEndpoint.NormalizeHost(restEndpoint, nameof(restEndpoint));
+
+ return new WeaviateClientBuilder()
+ .WithRestEndpoint(host)
+ .WithGrpcEndpoint($"grpc-{host}")
.WithRestPort(443)
.WithGrpcPort(443)
.UseSsl(true)
.WithCredentials(string.IsNullOrEmpty(apiKey) ? null : Auth.ApiKey(apiKey))
.WithHeaders(headers)
.WithHttpMessageHandler(httpMessageHandler);
+ }
///
/// Adds the rest endpoint using the specified endpoint
From c1f23752a3f55c05a59b62f911134336c3fe560e Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Fri, 18 Sep 2026 15:15:48 +0200
Subject: [PATCH 2/2] docs: add the changelog entry for the Weaviate Cloud URL
fix
---
CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab3de62d..baace2d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Fixed
+
+- **Weaviate Cloud Helpers Rejected the Cluster URL** — `Connect.Cloud`, `WeaviateClientBuilder.Cloud` and the endpoint overloads of `AddWeaviateCloud` used the endpoint verbatim as the host, so the cluster URL the Weaviate Cloud console shows (`https://my-cluster.weaviate.cloud`) failed with `UriFormatException`. They now accept that URL or a bare hostname and keep only the host, always connecting over TLS on port 443, and throw `ArgumentException` for any other scheme, embedded credentials, a port other than 443 or an invalid hostname (the DI overloads at registration).
+
---
## [1.2.0] — 2026-08-21