fullstackhero/dotnet-starter-kit · error · InvalidOperationException

Origin URL is not configured.

Error message

Origin URL is not configured.

What it means

ForgotPasswordCommandHandler requires the app's configured OriginUrl (used to build the password-reset link embedded in the email). When the Origin options section is missing or OriginUrl is unset/blank, the handler deliberately fails fast with InvalidOperationException instead of sending an email with a null/relative link.

Solutions

  1. Set OriginUrl in appsettings.json under the Origin section (e.g. "Origin": { "Url": "https://your-host" } per the options binding) or via env var Origin__Url.
  2. Verify the options are bound at startup: services.Configure<OriginOptions>(config.GetSection(...)) exists in the Identity/API registration.
  3. Confirm IOptions<OriginOptions>.Value.OriginUrl is a valid absolute URI; fix malformed URLs that fail to parse into Uri.
  4. Add a startup validation (IValidateOptions or AddOptions().Validate...) so a missing origin is caught at boot, not on first forgot-password call.

Example fix

// before (appsettings.json)
{
  "Database": { "ConnectionString": "..." }
}
// after
{
  "Origin": {
    "Url": "https://api.myapp.com"
  }
}
Defensive patterns

Strategy: validation

Validate before calling

var origin = originOptions.Value?.OriginUrl?.ToString();
if (string.IsNullOrWhiteSpace(origin))
    throw new InvalidOperationException("Origin URL is not configured (check Origin:Url in appsettings / Origin__Url env var).");

Type guard

bool HasOrigin(OriginOptions? o) => Uri.TryCreate(o?.OriginUrl?.ToString(), UriKind.Absolute, out var u) && (u.Scheme is "http" or "https");

Try / catch

try { await mediator.Send(new ForgotPasswordCommand(email)); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Origin URL is not configured")) {
    logger.LogError(ex, "Origin not configured");
    return Problem(statusCode: 500, title: "Server misconfiguration: origin URL missing");
}

Prevention

When it happens

Trigger: Calling POST forgot-password when appsettings has no Origin section, the OriginUrl key is empty, or the strongly-typed options were never bound (e.g. services.Configure<OriginOptions> not registered or section name typo).

Common situations: Deploying to a fresh environment where only Database/ConnectionStrings were copied over; a config refactor renamed OriginUrl; running the API locally without the Origin appsettings block; Docker deployments missing the Origin__OriginUrl env var.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/a81171521cfb48da. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Users/ForgotPassword/ForgotPasswordCommandHandler.cs:27

public sealed class ForgotPasswordCommandHandler : ICommandHandler<ForgotPasswordCommand, string>
{
    private readonly IUserService _userService;
    private readonly IOptions<OriginOptions> _originOptions;

    public ForgotPasswordCommandHandler(IUserService userService, IOptions<OriginOptions> originOptions)
    {
        _userService = userService;
        _originOptions = originOptions;
    }

    public async ValueTask<string> Handle(ForgotPasswordCommand command, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(command);

        var origin = _originOptions.Value?.OriginUrl?.ToString();
        if (string.IsNullOrWhiteSpace(origin))
        {
            throw new InvalidOperationException("Origin URL is not configured.");
        }

        await _userService.ForgotPasswordAsync(command.Email, origin, cancellationToken).ConfigureAwait(false);

        return "Password reset email sent.";
    }
}

View on GitHub (pinned to 3f2959e683)