{"record":{"id":"3efa98523be6a0f7","repo":"tursodatabase/turso","slug":"the-sync-remote-url-must-not-include-a-query-strin","errorCode":null,"errorMessage":"The sync remote URL must not include a query string or fragment.","messagePattern":"The sync remote URL must not include a query string or fragment\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data/TursoSyncDatabaseOptions.cs","lineNumber":128,"sourceCode":"    public Uri RemoteUri { get; }\n    public string? AuthToken { get; init; }\n    public string ClientName { get; init; } = \"turso-sync-dotnet\";\n    public TimeSpan? LongPollTimeout { get; init; }\n    public bool BootstrapIfEmpty { get; init; } = true;\n    public TursoPartialSyncOptions? PartialSync { get; init; }\n    public TursoRemoteEncryptionOptions? RemoteEncryption { get; init; }\n    public long? PushOperationsThreshold { get; init; }\n    public long? PullBytesThreshold { get; init; }\n    public bool ForceLogicalMvccPull { get; init; }\n    public HttpClient? HttpClient { get; init; }\n    public string? ExperimentalFeatures { get; init; }\n\n    internal Uri GetNormalizedRemoteUri()\n    {\n        if (!RemoteUri.IsAbsoluteUri)\n            throw new ArgumentException(\"The sync remote URL must be absolute.\", nameof(RemoteUri));\n        if (!string.IsNullOrEmpty(RemoteUri.Query) || !string.IsNullOrEmpty(RemoteUri.Fragment))\n            throw new ArgumentException(\"The sync remote URL must not include a query string or fragment.\", nameof(RemoteUri));\n        if (!string.IsNullOrEmpty(RemoteUri.UserInfo))\n            throw new ArgumentException(\"Use AuthToken instead of embedding credentials in the sync URL.\", nameof(RemoteUri));\n        if (string.IsNullOrEmpty(RemoteUri.Host))\n            throw new ArgumentException(\"The sync remote URL must include a host.\", nameof(RemoteUri));\n\n        var scheme = RemoteUri.Scheme.ToLowerInvariant() switch\n        {\n            \"turso\" or \"libsql\" => Uri.UriSchemeHttps,\n            \"http\" => Uri.UriSchemeHttp,\n            \"https\" => Uri.UriSchemeHttps,\n            _ => throw new ArgumentException(\n                \"The sync remote URL must use turso, libsql, HTTP, or HTTPS.\",\n                nameof(RemoteUri)),\n        };\n        var builder = new UriBuilder(RemoteUri)\n        {\n            Scheme = scheme,\n            Port = RemoteUri.IsDefaultPort ? -1 : RemoteUri.Port,","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/bindings/dotnet/src/Turso.Data/TursoSyncDatabaseOptions.cs#L110-L146","documentation":"GetNormalizedRemoteUri rejects a RemoteUri that carries a query string (?key=value) or a fragment (#anchor). Sync URLs must identify only the remote database endpoint; the library builds its own request URIs and any query or fragment would corrupt them. The check runs before any network call, when the options are validated or normalized.","triggerScenarios":"Constructing TursoSyncDatabaseOptions (or calling TursoSyncDatabase/configuration paths that invoke GetNormalizedRemoteUri/Validate) with a RemoteUri such as https://host/db?slug=foo or https://host/db#frag. Uri.Query or Uri.Fragment is non-empty at TursoSyncDatabaseOptions.cs:127-128.","commonSituations":"Pasting a Turso web-console URL that includes query parameters, appending API keys or tokens to the URL as query parameters, copying a URL with an anchor from documentation or a browser address bar, template code that appends database names as '?database=x'.","solutions":["Strip the '?' query and '#' fragment from the remote URL before passing it as RemoteUri.","Move any token that was placed in the query string into the AuthToken option instead.","Recreate the Uri with only scheme, host, port, and path components (e.g. new Uri(builder.Scheme + builder.Host + builder.Path))."],"exampleFix":"// before\nvar opts = new TursoSyncDatabaseOptions(path, new Uri(\"https://mydb.turso.io?token=abc\"));\n// after\nvar opts = new TursoSyncDatabaseOptions(path, new Uri(\"https://mydb.turso.io\")) { AuthToken = \"abc\" };","handlingStrategy":"validation","validationCode":"static void EnsureNoQueryOrFragment(Uri remoteUri)\n{\n    if (!remoteUri.IsAbsoluteUri) throw new ArgumentException(\"URL must be absolute\");\n    if (!string.IsNullOrEmpty(remoteUri.Query) || !string.IsNullOrEmpty(remoteUri.Fragment))\n        throw new ArgumentException($\"Sync URL must not contain query/fragment: {remoteUri}\");\n}","typeGuard":"static bool IsCleanSyncUrl(Uri u) => u.IsAbsoluteUri && string.IsNullOrEmpty(u.Query) && string.IsNullOrEmpty(u.Fragment) && string.IsNullOrEmpty(u.UserInfo);","tryCatchPattern":"try { var db = new TursoSyncDatabase(opts); }\ncatch (ArgumentException ex) when (ex.ParamName == nameof(TursoSyncDatabaseOptions.RemoteUri))\n{\n    // fix URL: strip query/fragment, then retry\n}","preventionTips":["Never append tokens or options as query parameters to the sync URL; use AuthToken and options properties.","Normalize URLs once in a shared factory that strips '?' and '#' before constructing options.","Log the RemoteUri in a startup assertion test so bad URLs surface in CI, not production."],"tags":["dotnet","sync","url-validation","argument-exception"],"backgroundTag":"invalid-sync-url","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-31T11:17:35.598Z","contentChangedAt":"2026-08-31T11:17:35.598Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}