{"record":{"id":"b6d48b5284904046","repo":"FiloSottile/mkcert","slug":"failed-deleting-certificate-v","errorCode":null,"errorMessage":"failed deleting certificate: %v","messagePattern":"failed deleting certificate: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"truststore_windows.go","lineNumber":131,"sourceCode":"\t\tcertPtr, _, err := procCertEnumCertificatesInStore.Call(uintptr(w), uintptr(unsafe.Pointer(cert)))\n\t\tif cert = (*syscall.CertContext)(unsafe.Pointer(certPtr)); cert == nil {\n\t\t\tif errno, ok := err.(syscall.Errno); ok && errno == 0x80092004 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn deletedAny, fmt.Errorf(\"failed enumerating certs: %v\", err)\n\t\t}\n\t\t// Parse cert\n\t\tcertBytes := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length]\n\t\tparsedCert, err := x509.ParseCertificate(certBytes)\n\t\t// We'll just ignore parse failures for now\n\t\tif err == nil && parsedCert.SerialNumber != nil && parsedCert.SerialNumber.Cmp(serial) == 0 {\n\t\t\t// Duplicate the context so it doesn't stop the enum when we delete it\n\t\t\tdupCertPtr, _, err := procCertDuplicateCertificateContext.Call(uintptr(unsafe.Pointer(cert)))\n\t\t\tif dupCertPtr == 0 {\n\t\t\t\treturn deletedAny, fmt.Errorf(\"failed duplicating context: %v\", err)\n\t\t\t}\n\t\t\tif ret, _, err := procCertDeleteCertificateFromStore.Call(dupCertPtr); ret == 0 {\n\t\t\t\treturn deletedAny, fmt.Errorf(\"failed deleting certificate: %v\", err)\n\t\t\t}\n\t\t\tdeletedAny = true\n\t\t}\n\t}\n\treturn deletedAny, nil\n}\n","sourceCodeStart":113,"sourceCodeEnd":138,"githubUrl":"https://github.com/FiloSottile/mkcert/blob/1c1dc4ed27ed5936046b6398d39cab4d657a2d8e/truststore_windows.go#L113-L138","documentation":"After duplicating a matching context, mkcert calls CertDeleteCertificateFromStore on the duplicate; a zero return means Windows refused the deletion. CertDeleteCertificateFromStore both removes the cert and frees the context, and it fails most often with access-denied: the ROOT store was opened without sufficient privilege to delete, or policy/AV blocks trusted-root modification.","triggerScenarios":"`mkcert -uninstall` from a non-elevated shell when the ROOT store requires admin to modify; enterprise policy protecting trusted roots; the certificate being pinned/protected by Windows (e.g. a curated root) so deletion is denied.","commonSituations":"Developer runs uninstall in a normal terminal after originally installing from an elevated one; hardened corporate images where only administrators may remove trusted roots; scripts/CI cleanup steps running as restricted users.","solutions":["Relaunch the terminal as Administrator and rerun `mkcert -uninstall`.","If policy blocks programmatic deletion, remove the mkcert CA manually via certmgr.msc (Trusted Root Certification Authorities) or `certutil -delstore ROOT <serial>` as admin.","In CI, run the cleanup step under an account with cert-store write/delete rights.","Retry once after closing certmgr/other tools holding the store open."],"exampleFix":"# before (non-elevated)\nmkcert -uninstall   # failed deleting certificate: Access is denied.\n\n# after (elevated) or manual\ncertutil -delstore ROOT <serial-of-mkcert-ca>","handlingStrategy":"validation","validationCode":"if runtime.GOOS == \"windows\" && !isElevated() {\n    return errors.New(\"run `mkcert -uninstall` from an elevated terminal\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Match privileges between install and uninstall (both elevated).","In automation, run cleanup under an account with cert-store delete rights.","Know the manual escape hatch: certmgr.msc or `certutil -delstore ROOT <serial>`.","Close tools that hold the store open (certmgr, certutil) before retrying."],"tags":["mkcert","windows","crypt32","access-denied","uninstall","certificate-store"],"backgroundTag":null,"analyzedSha":"1c1dc4ed27ed5936046b6398d39cab4d657a2d8e","analyzedAt":"2026-08-15T09:28:09.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}