{"record":{"id":"62fab9ad1aed1beb","repo":"microsoft/aspire","slug":"the-developer-certificate-could-not-be-cached-because-it-was","errorCode":null,"errorMessage":"The developer certificate could not be cached because it was not valid.","messagePattern":"The developer certificate could not be cached because it was not valid\\.","errorType":"exception","errorClass":"DcpDeveloperCertificateUnavailableException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Utils/EnvironmentChecker/DcpDeveloperCertificateCache.cs","lineNumber":18,"sourceCode":"// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nusing System.Security.Cryptography.X509Certificates;\nusing Aspire.Cli.Certificates;\nusing Aspire.Cli.Resources;\nusing Aspire.Hosting.Utils;\nusing Microsoft.AspNetCore.Certificates.Generation;\n\nnamespace Aspire.Cli.Utils.EnvironmentChecker;\n\ninternal static class DcpDeveloperCertificateCache\n{\n    public static string EnsureDeveloperCertificateCache(CertificateManager certificateManager, X509Certificate2 certificate)\n    {\n        if (!certificate.IsAspNetCoreDevelopmentCertificate() || string.IsNullOrWhiteSpace(certificate.Thumbprint))\n        {\n            throw new DcpDeveloperCertificateUnavailableException(DoctorCommandStrings.DcpDeveloperCertificateInvalidForCacheDetails);\n        }\n\n        var cacheDirectory = CertificateHelpers.AspireDevCertsHttpsCacheDirectory;\n        if (!Path.IsPathFullyQualified(cacheDirectory))\n        {\n            throw new DcpDeveloperCertificateUnavailableException(DoctorCommandStrings.DcpDeveloperCertificateUserProfileMissingDetails);\n        }\n\n        var lookup = CertificateHelpers.GetAspireCertificateHash(certificate);\n        var certificatePath = Path.Combine(cacheDirectory, $\"{lookup}.crt\");\n        var keyPath = Path.ChangeExtension(certificatePath, \".key\");\n\n        // The public certificate export does not require private key access, so older caches with\n        // a key but no certificate can be filled without re-exporting the cached key.\n        certificateManager.ExportCertificate(certificate, certificatePath, includePrivateKey: !File.Exists(keyPath), password: null, CertificateKeyExportFormat.Pem);\n\n        return certificatePath;\n    }","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Utils/EnvironmentChecker/DcpDeveloperCertificateCache.cs#L1-L36","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Ensure you pass the certificate produced by CertificateManager's dev-cert lookup rather than an arbitrary certificate from the store.","Verify the cert's identity (subject/OID matching ASP.NET Core dev cert) before caching it.","Rerun the doctor workflow after regeneration."],"exampleFix":"// before\nvar cert = store.Certificates.First(c => c.HasPrivateKey);\nvar path = DcpDeveloperCertificateCache.EnsureDeveloperCertificateCache(manager, cert);\n// after\nvar cert = manager.GetCertificates().FirstOrDefault(c =>\n    c.IsAspNetCoreDevelopmentCertificate() && c.HasPrivateKey &&\n    manager.GetTrustLevel(c) == CertificateManager.TrustLevel.Full);\nvar path = DcpDeveloperCertificateCache.EnsureDeveloperCertificateCache(manager, cert);","handlingStrategy":"validation","validationCode":"bool cacheable = certificate.IsAspNetCoreDevelopmentCertificate() && !string.IsNullOrWhiteSpace(certificate.Thumbprint);\nif (!cacheable) throw new InvalidOperationException(\"Certificate is not the ASP.NET Core dev cert; regenerate with dotnet dev-certs.\");","typeGuard":"static bool IsCacheableDevCert(X509Certificate2 c) => c.IsAspNetCoreDevelopmentCertificate() && !string.IsNullOrWhiteSpace(c.Thumbprint);","tryCatchPattern":"try { var path = DcpDeveloperCertificateCache.EnsureDeveloperCertificateCache(manager, cert); }\ncatch (DcpDeveloperCertificateUnavailableException ex) when (ex.Message.Contains(\"not valid\")) { /* regenerate cert via dotnet dev-certs and retry */ }","preventionTips":["Only pass certificates obtained through CertificateManager's dev-cert lookup into the cache helper.","Ensure the store contains the genuine ASP.NET Core dev certificate (dotnet dev-certs).","Validate cert identity/subject before caching."],"tags":["dcp","certificate","cache","dev-certs"],"backgroundTag":"developer-certificate-untrusted","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}