microsoft/aspire · error · DcpDeveloperCertificateUnavailableException

The developer certificate could not be cached because it…

Error message

The developer certificate could not be cached because it was not valid.

What it means

DcpDeveloperCertificateCache.EnsureDeveloperCertificateCache exports the trusted developer certificate into Aspire's dev-certs cache so DCP can consume it. It first validates that the supplied X509Certificate2 really is the ASP.NET Core development certificate and has a thumbprint; otherwise it throws DcpDeveloperCertificateUnavailableException saying the certificate is invalid for caching.

Solutions

  1. Regenerate the canonical certificate with `dotnet dev-certs https --clean` followed by `dotnet dev-certs https --trust` so the store contains the genuine ASP.NET Core dev cert.
  2. Ensure you pass the certificate produced by CertificateManager's dev-cert lookup rather than an arbitrary certificate from the store.
  3. Verify the cert's identity (subject/OID matching ASP.NET Core dev cert) before caching it.
  4. Rerun the doctor workflow after regeneration.

Example fix

// before
var cert = store.Certificates.First(c => c.HasPrivateKey);
var path = DcpDeveloperCertificateCache.EnsureDeveloperCertificateCache(manager, cert);
// after
var cert = manager.GetCertificates().FirstOrDefault(c =>
    c.IsAspNetCoreDevelopmentCertificate() && c.HasPrivateKey &&
    manager.GetTrustLevel(c) == CertificateManager.TrustLevel.Full);
var path = DcpDeveloperCertificateCache.EnsureDeveloperCertificateCache(manager, cert);
Defensive patterns

Strategy: validation

Validate before calling

bool cacheable = certificate.IsAspNetCoreDevelopmentCertificate() && !string.IsNullOrWhiteSpace(certificate.Thumbprint);
if (!cacheable) throw new InvalidOperationException("Certificate is not the ASP.NET Core dev cert; regenerate with dotnet dev-certs.");

Type guard

static bool IsCacheableDevCert(X509Certificate2 c) => c.IsAspNetCoreDevelopmentCertificate() && !string.IsNullOrWhiteSpace(c.Thumbprint);

Try / catch

try { var path = DcpDeveloperCertificateCache.EnsureDeveloperCertificateCache(manager, cert); }
catch (DcpDeveloperCertificateUnavailableException ex) when (ex.Message.Contains("not valid")) { /* regenerate cert via dotnet dev-certs and retry */ }

Prevention

When it happens

Trigger: DcpConnectionChecker passes a certificate to EnsureDeveloperCertificateCache that fails IsAspNetCoreDevelopmentCertificate() or has null/whitespace Thumbprint - i.e. the certificate chosen from the store is not the actual ASP.NET Core HTTPS dev cert.

Common situations: Store enumeration picking up a look-alike/self-signed certificate that is not the real dev cert, an expired dev cert replaced by a different issuer, or a cert created by a tool other than `dotnet dev-certs` with a non-standard subject/OID.

Understand the failure class

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/62fab9ad1aed1beb. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Cli/Utils/EnvironmentChecker/DcpDeveloperCertificateCache.cs:18

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Security.Cryptography.X509Certificates;
using Aspire.Cli.Certificates;
using Aspire.Cli.Resources;
using Aspire.Hosting.Utils;
using Microsoft.AspNetCore.Certificates.Generation;

namespace Aspire.Cli.Utils.EnvironmentChecker;

internal static class DcpDeveloperCertificateCache
{
    public static string EnsureDeveloperCertificateCache(CertificateManager certificateManager, X509Certificate2 certificate)
    {
        if (!certificate.IsAspNetCoreDevelopmentCertificate() || string.IsNullOrWhiteSpace(certificate.Thumbprint))
        {
            throw new DcpDeveloperCertificateUnavailableException(DoctorCommandStrings.DcpDeveloperCertificateInvalidForCacheDetails);
        }

        var cacheDirectory = CertificateHelpers.AspireDevCertsHttpsCacheDirectory;
        if (!Path.IsPathFullyQualified(cacheDirectory))
        {
            throw new DcpDeveloperCertificateUnavailableException(DoctorCommandStrings.DcpDeveloperCertificateUserProfileMissingDetails);
        }

        var lookup = CertificateHelpers.GetAspireCertificateHash(certificate);
        var certificatePath = Path.Combine(cacheDirectory, $"{lookup}.crt");
        var keyPath = Path.ChangeExtension(certificatePath, ".key");

        // The public certificate export does not require private key access, so older caches with
        // a key but no certificate can be filled without re-exporting the cached key.
        certificateManager.ExportCertificate(certificate, certificatePath, includePrivateKey: !File.Exists(keyPath), password: null, CertificateKeyExportFormat.Pem);

        return certificatePath;
    }

View on GitHub (pinned to 25830f84bd)