reactiveui/refit · error · ArgumentException

The [Url] parameter value "{value}" must be an absolute URI

Error message

The [Url] parameter value "{value}" must be an absolute URI (for example "https://host/path").

What it means

RequireAbsoluteUrl rejects a [Url] parameter whose value is not an absolute URI. The method accepts a string or Uri, takes the original string form, and uses Uri.TryCreate with UriKind.Absolute; any null, empty, relative, or malformed value yields an ArgumentException pointing at the offending value in the message.

Source

Thrown at src/Refit/GeneratedRequestRunner.cs:81

        var basePath = client.BaseAddress?.AbsolutePath
                       ?? throw new InvalidOperationException("BaseAddress must be set on the HttpClient instance");
        basePath = basePath == "/" ? string.Empty : basePath.TrimEnd('/');
        var absolute = new Uri(QueryUriFormatBase, basePath + relativePath);
        return new(absolute.GetComponents(UriComponents.PathAndQuery, queryUriFormat), UriKind.Relative);
    }

    /// <summary>Validates that a <c>[Url]</c> parameter value is an absolute URI, returning its string form as the
    /// base for a full-URL request that bypasses the client's base address. A <see cref="string"/> value is used as
    /// written; a <see cref="Uri"/> value contributes its <see cref="Uri.OriginalString"/>.</summary>
    /// <param name="url">The <c>[Url]</c> parameter value: a <see cref="string"/> or a <see cref="Uri"/>.</param>
    /// <returns>The absolute URI's string form.</returns>
    /// <exception cref="ArgumentException"><paramref name="url"/> is <see langword="null"/>, empty, or not an absolute URI.</exception>
    public static string RequireAbsoluteUrl(object? url)
    {
        var text = url is Uri uri ? uri.OriginalString : url as string;
        return Uri.TryCreate(text, UriKind.Absolute, out _)
            ? text!
            : throw new ArgumentException(FormatAbsoluteUrlError(url), nameof(url));
    }

    /// <summary>Builds the request path for a generated request from a template.</summary>
    /// <param name="relativePathTemplate">The method's relative path, including any leading slash and query string.</param>
    /// <param name="allowUnmatchedParameter">Whether to allow unmatched URL parameters.</param>
    /// <param name="uriParams">The replacement uri parameters, ordered by template position.</param>
    /// <returns>A path with all the placeholder parameters in the path template replaced.</returns>
    /// <exception cref="ArgumentException">
    /// A URI template parameter is not available in the provided parameter span and unmatched URL parameters aren't allowed.
    /// </exception>
    /// <remarks>Generated call sites pass the replacements as a collection expression. On C# 12 and a runtime with inline
    /// array support (net8.0+) that materializes on the stack, so any number of path parameters is expanded without a heap
    /// allocation; older consumers pass a small array via the same signature.</remarks>
    public static string BuildRequestPath(
        string relativePathTemplate,
        bool allowUnmatchedParameter,
        ReadOnlySpan<((int StartIdx, int EndIdx) Range, string? Value)> uriParams)
    {

View on GitHub (pinned to b455f65ecc)

Solutions

  1. Pass a fully-qualified URL string such as "https://host/api/resource" to the [Url] parameter
  2. If passing a Uri, construct it with UriKind.Absolute (e.g. new Uri("https://host/path", UriKind.Absolute))
  3. Validate or default the URL at the call site before invoking the method
  4. If you meant a relative path under the base address, remove [Url] from the parameter and use the normal route template instead

Example fix

// before
Task<Thing> GetAsync([Url] string url);
await api.GetAsync("/things/42"); // relative => throws

// after
await api.GetAsync("https://api.example.com/things/42");
Defensive patterns

Strategy: validation

Validate before calling

static string EnsureAbsoluteUrl(string url) =>
    Uri.TryCreate(url, UriKind.Absolute, out _)
        ? url
        : throw new ArgumentException($"Expected an absolute URL, got: {url}", nameof(url));

await api.GetAsync(EnsureAbsoluteUrl(candidate));

Type guard

static bool IsAbsoluteUrl(string? url) =>
    !string.IsNullOrWhiteSpace(url) && Uri.TryCreate(url, UriKind.Absolute, out _);

Try / catch

try { await api.GetAsync(url); }
catch (ArgumentException ex) when (ex.Message.Contains("must be an absolute URI"))
{
    // log the bad URL and prompt the user / fall back to a configured host
}

Prevention

When it happens

Trigger: An interface method has a parameter marked [Url] and the caller passes a relative path (e.g. "/foo"), a scheme-less string, a null, or a Uri created with UriKind.Relative. Used to bypass the client base address, the value must carry its own scheme and host.

Common situations: Passing a path fragment where a full URL is expected; reading a URL from config that came back null/empty; supplying a Uri built from a relative string; dynamically building the URL and accidentally stripping the scheme.

Related errors


AI-assisted analysis of reactiveui/refit@b455f65ecc (2026-08-13). Data as JSON: /api/errors/705915471ff975ba. Report an issue: GitHub.