{"record":{"id":"9c3c3c0eb61c1a5c","repo":"caddyserver/caddy","slug":"saving-root-key-v","errorCode":null,"errorMessage":"saving root key: %v","messagePattern":"saving root key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"modules/caddypki/ca.go","lineNumber":339,"sourceCode":"\trootCert, rootKey, err = generateRoot(repl.ReplaceAll(ca.RootCommonName, \"\"))\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"generating CA root: %v\", err)\n\t}\n\trootCertPEM, err := pemEncodeCert(rootCert.Raw)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"encoding root certificate: %v\", err)\n\t}\n\terr = ca.storage.Store(ca.ctx, ca.storageKeyRootCert(), rootCertPEM)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"saving root certificate: %v\", err)\n\t}\n\trootKeyPEM, err := certmagic.PEMEncodePrivateKey(rootKey)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"encoding root key: %v\", err)\n\t}\n\terr = ca.storage.Store(ca.ctx, ca.storageKeyRootKey(), rootKeyPEM)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"saving root key: %v\", err)\n\t}\n\n\treturn rootCert, rootKey, nil\n}\n\nfunc (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey crypto.Signer) (interCertChain []*x509.Certificate, interKey crypto.Signer, err error) {\n\tvar interCert *x509.Certificate\n\tinterCertPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyIntermediateCert())\n\tif err != nil {\n\t\tif !errors.Is(err, fs.ErrNotExist) {\n\t\t\treturn nil, nil, fmt.Errorf(\"loading intermediate cert: %v\", err)\n\t\t}\n\n\t\t// TODO: should we require that all or none of the assets are required before overwriting anything?\n\t\tinterCert, interKey, err = ca.genIntermediate(rootCert, rootKey)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"generating new intermediate cert: %v\", err)\n\t\t}","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddypki/ca.go#L321-L357","documentation":"The final step of genRoot stores the root key PEM at storageKeyRootKey. A storage write failure here aborts CA setup with 'saving root key'. Note the root cert may already be stored at this point, leaving intentionally-recoverable-but-inconsistent assets (see the TODO in this file about all-or-none assets), so the next start will find a cert without a key unless cleaned up.","triggerScenarios":"storage.Store(key) fails after Store(cert) succeeded: disk fills up mid-provisioning, backend drops between two writes, permissions change. First-boot-of-CA path only.","commonSituations":"Disk-full or quota-hit exactly during provisioning; flaky network storage; partially provisioned CA left behind after a crash, causing confusing follow-up errors (e.g. 586/587) on next start.","solutions":["Fix storage writability/capacity, then delete the partially provisioned CA directory in storage (cert without key) and restart so root generation re-runs atomically.","Monitor storage free space so provisioning cannot run out mid-write.","For custom backends, ensure writes are atomic and idempotent across restarts.","If assets must be kept, manually place a matching key.pem; otherwise regenerating is safer."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// After provisioning attempts, detect partial state (cert without key)\n_, e1 := os.Stat(caDir + \"/root.crt\")\n_, e2 := os.Stat(caDir + \"/key.pem\")\nif (e1 == nil) != (e2 == nil) { cleanupPartialCA(caDir) }","typeGuard":"func caStorageConsistent(dir string) bool {\n    _, c := os.Stat(filepath.Join(dir, \"root.crt\"))\n    _, k := os.Stat(filepath.Join(dir, \"key.pem\"))\n    return (c == nil) == (k == nil)\n}","tryCatchPattern":"if strings.Contains(err.Error(), \"saving root key\") {\n    // free space / fix backend, delete partial CA dir, restart to regenerate cleanly\n}","preventionTips":["Watch disk space alerts below a threshold that covers asset writes.","On provisioning failure, always clean partial CA assets before retrying.","Prefer storage backends with atomic writes for PKI data."],"tags":["pki","storage","root-key","partial-state"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}