Certes.AcmeException: Can not finalize order with status 'Invalid'

My domain is: usm.facefirst.net

We have been using Lets Encrypt from our services to get a new certificate but now it is getting this error.
We have not changed anything from our side so wondering if Let's encrypt has changed anything from their end.

The tlsserver and shortlived profiles started using their new root last month, but that's unlikely to impact you unless both (1) you explicitly chose to use one of those profiles (which I'm not sure Certes even handles yet) and (2) something is incorrectly configured in your client.

Can you please fill out the rest of the standard questionnaire? Especially the exact message you're getting and what command you're running to see it. If it's something graphical instead of command-line based, feel free to include screenshots. Also the last question about what the version is and what program you're using to manage your certificates.


My domain is: usm.facefirst.net

I ran this command:

It produced this output:

My web server is (include version):

The operating system my web server runs on is (include version):

My hosting provider, if applicable, is:

I can login to a root shell on my machine (yes or no, or I don't know):

I'm using a control panel to manage my site (no, or provide the name and version of the control panel):

The version of my client is (e.g. output of certbot --version or certbot-auto --version if you're using Certbot):

3 Likes

I ran this command:

private async Task<OrderInfo> CreateLE(string host, string account, Func<string, string, Exception, Task> failFunc, Func<string, Task> successFunc, int timeoutMs, CancellationToken cancellationToken)
{
    if (!HostHelper.IsDomain(host))
    {
        try { await failFunc($"Invalid domain {host}", host, null); } catch { }
        return null;
    }
    cancellationToken.ThrowIfCancellationRequested();
    var acme = new AcmeContext(_options.AcmeServer, KeyFactory.FromPem(account));

    _Log.Information("Lets Encrypt: Creating order for {Host}", host);

    cancellationToken.ThrowIfCancellationRequested();
    var order = await acme.NewOrder(new[] { host });
    cancellationToken.ThrowIfCancellationRequested();
    var authzs = (await order.Authorizations());
    var authz = authzs.First();
    var httpChallenge = await authz.Http();

    var orderInfo = new OrderInfo
    {
        Order = order,
        Challenge = httpChallenge,
        HostName = host,
        FailFunc = failFunc,
        SuccessFunc = successFunc,
        CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(timeoutMs).Token)
    };

    _ = Task.Run(async () => await orderInfo.TimeoutMonitorAsync());

    cancellationToken.ThrowIfCancellationRequested();
    _httpChallengeResponseStore.AddChallengeResponse(httpChallenge.Token, orderInfo);
    cancellationToken.ThrowIfCancellationRequested();
    await httpChallenge.Validate();

    // Even if the challenges never come, attempt to finish creating the certificate after 30 seconds.
    // We do this because, if you request a duplicate certificate within a certain time period, there will be no
    // new challenges issued by Let's Encrypt.
    await Task.Run(async () =>
    {
        // Give some time to Let´s Encrypt to send challenges and process our responses.
        await Task.Delay(30 * 1000, orderInfo.CancellationTokenSource.Token);
        if (!orderInfo.CancellationTokenSource.Token.IsCancellationRequested)
        {
            await BuildCertificate(orderInfo, orderInfo.HostName);
        }
        else
        {
            await orderInfo.FailedAsync($"Domain validation cancelled for {orderInfo.HostName}", orderInfo.HostName, null);
        }
    });

    return orderInfo;
}

It produced this output:

2026-02-09 11:38:15.023 -06:00 [FTL] (XA418/8) Application terminated unexpectedly
System.AggregateException: One or more errors occurred. (Can not finalize order with status 'Invalid'.)
---> Certes.AcmeException: Can not finalize order with status 'Invalid'.
   at Certes.IOrderContextExtensions.Generate(IOrderContext context, CsrInfo csr, IKey key, String preferredChain, Int32 retryCount)
   at LetsEncrypt.CertificateRequestor.BuildCertificate(OrderInfo orderInfo, String hostName)
   at LetsEncrypt.CertificateRequestor.BuildCertificate(OrderInfo orderInfo, String hostName)
   at LetsEncrypt.CertificateRequestor.<>c__DisplayClass22_0.<<CreateLE>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at LetsEncrypt.CertificateRequestor.CreateLE(String host, String account, Func`4 failFunc, Func`2 successFunc, Int32 timeoutMs, CancellationToken cancellationToken)
   at LetsEncrypt.CertificateRequestor.<>c__DisplayClass15_0.<<RefreshCertificates>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at LetsEncrypt.CertificateRequestor.<>c__DisplayClass15_0.<<RefreshCertificates>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at LetsEncrypt.SemaphoreLock.TryLockAsync(Int32 timeoutMs, Func`1 worker)
   at LetsEncrypt.CertificateRequestor.RefreshCertificates(Func`4 failFunc, Func`2 successFunc, Int32 timeoutMs, CancellationToken cancellationToken)
   at LetsEncrypt.DomainManager.SetActive(DomainInfo[] domains, Func`4 failFunc, Func`2 successFunc, Func`2 preFunc, Func`2 postFunc, Int32 timeoutMs, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at LetsEncrypt.Extensions.UseLetsEncrypt(IApplicationBuilder app, IServiceProvider serviceProvider, DomainInfo[] domains, Func`2 preFunc, Func`2 postFunc)
   at FaceFirst.LetsEncrypt.LetsEncryptExtensions.UseFaceFirstLetsEncrypt(IApplicationBuilder app, IServiceProvider serviceProvider, DomainInfo[] domains, Func`2 preFunc, Func`2 postFunc)
   at FaceFirst.Deployment.Server.Program.Main(String[] args)

My web server is (include version): Kestral

The operating system my web server runs on is (include version): Windows Server 2019 Standard

My hosting provider, if applicable, is: WebNx

I can login to a root shell on my machine (yes or no, or I don't know): Yes

I'm using a control panel to manage my site (no, or provide the name and version of the control panel): No

The version of my client is (e.g. output of certbot --version or certbot-auto --version if you're using Certbot): We are not using the Certbot

We have this legacy app where there is a middleware C# package that we use to manage our certificates when used via Let's Encrypt.
That we have not changed in years.

I'm not familiar with that library, but it looks like your code is proceeding to try to finalize the order even though the challenge validation has failed. I'm guessing there is some error you could look up on the challenge that it should be doing before it tries to send the finalize request, and that the challenge's error would be telling you what's actually going wrong. You may need to wait for someone else (@webprofusion maybe?) who knows more about the C# ACME libraries than I do.

As a first-pass at a guess though, if we can't get the actual error that the server is returning: Are you expecting that domain name to be publicly available? That code looks like it's trying to do the HTTP-01 challenge, which requires the domain name to be accessible for http on port 80. But it doesn't look like it's public to me, nor from Let's Debug, nor from check-host.net systems.

6 Likes

@VivekP as @petercooperjr says your example code doesn't check the status of the authorization.

Once you complete a challenge (present the http challenge response on your domain) you submit the challenge for authorization, then you should wait (while the CA checks your answer) for the authorization to become valid or invalid. You're not waiting at all and you're not checking the status of the authorization or the order before you attempt to finalize it.

Most likely the http challenge is failing because you are now blocking incoming http (TCP port 80) requests (either all or just geographically etc) . If you use HTTP challenges you need to keep port 80 open for your challenge responses to be served, or redirect to https.

5 Likes